From 5bcd1bf9cd49f533febe55405bb54a3c5285ab3c Mon Sep 17 00:00:00 2001 From: Kral Date: Mon, 5 Oct 2026 18:02:52 +0200 Subject: [PATCH] Stage 2 summary at 50 accepted trajectories Co-Authored-By: Claude Sonnet 5.5 --- docs/yol-haritasi.md | 12 + .../train/G1042/faulty/m0_snow_fee.clas.abap | 93 +++++ .../train/G1042/faulty/m1_snow_fee.clas.abap | 93 +++++ .../train/G1042/faulty/m2_snow_fee.clas.abap | 93 +++++ .../train/G1042/faulty/m3_snow_fee.clas.abap | 93 +++++ .../train/G1042/faulty/m4_snow_fee.clas.abap | 93 +++++ tasks_gen/train/G1042/generation.json | 24 ++ .../G1042/hidden/snow_fee_hidden.clas.abap | 16 +- tasks_gen/train/G1042/mutation.json | 93 +++++ .../train/G1042/reference/snow_fee.clas.abap | 37 +- .../G1042/reference/snow_fee.testclasses.abap | 14 +- tasks_gen/train/G1042/seed/snow_fee.clas.abap | 1 - .../G1044/faulty/m2_escape_checker.clas.abap | 113 ++++++ .../G1044/faulty/m3_escape_checker.clas.abap | 113 ++++++ .../G1044/faulty/m4_escape_checker.clas.abap | 113 ++++++ tasks_gen/train/G1044/generation.json | 24 ++ tasks_gen/train/G1044/mutation.json | 66 ++++ .../G1045/faulty/m0_cinema_pricer.clas.abap | 110 ++++++ .../G1045/faulty/m1_cinema_pricer.clas.abap | 110 ++++++ .../G1045/faulty/m2_cinema_pricer.clas.abap | 110 ++++++ .../G1045/faulty/m3_cinema_pricer.clas.abap | 110 ++++++ .../G1045/faulty/m4_cinema_pricer.clas.abap | 110 ++++++ tasks_gen/train/G1045/generation.json | 19 + .../train/G1045/hidden/t21_hidden.clas.abap | 217 ++++++++++++ tasks_gen/train/G1045/mutation.json | 77 +++++ .../G1045/reference/cinema_pricer.clas.abap | 110 ++++++ .../reference/cinema_pricer.testclasses.abap | 95 +++++ .../G1045/seed/if_cinema_price.intf.abap | 42 +++ tasks_gen/train/G1045/spec.md | 62 ++++ tasks_gen/train/G1045/task.json | 51 +++ tasks_gen/train/G1047/generation.json | 34 ++ .../G1047/reference/gravel_pricer.clas.abap | 98 ++++++ .../reference/gravel_pricer.testclasses.abap | 103 ++++++ tasks_gen/train/G1047/seed/grv_ord.tabl.abap | 9 + tasks_gen/train/G1047/seed/seed_grv.clas.abap | 19 + tasks_gen/train/G1047/spec.md | 63 ++++ tasks_gen/train/G1047/task.json | 68 ++++ .../train/G1048/faulty/m0_i_truck.ddls.asddls | 14 + .../train/G1048/faulty/m1_i_truck.ddls.asddls | 14 + .../train/G1048/faulty/m2_i_truck.ddls.asddls | 14 + .../train/G1048/faulty/m3_i_truck.ddls.asddls | 14 + tasks_gen/train/G1048/generation.json | 29 ++ .../train/G1048/hidden/t21_hidden.clas.abap | 95 +++++ tasks_gen/train/G1048/mutation.json | 57 +++ .../train/G1048/reference/i_truck.ddls.asddls | 14 + .../train/G1048/reference/t21_test.clas.abap | 51 +++ tasks_gen/train/G1048/seed/depot.tabl.asabap | 11 + .../train/G1048/seed/seed_data.clas.abap | 25 ++ tasks_gen/train/G1048/seed/truck.tabl.asabap | 12 + tasks_gen/train/G1048/spec.md | 53 +++ tasks_gen/train/G1048/task.json | 72 ++++ .../G1050/faulty/m0_tailor_pricer.clas.abap | 126 +++++++ .../G1050/faulty/m1_tailor_pricer.clas.abap | 126 +++++++ .../G1050/faulty/m2_tailor_pricer.clas.abap | 126 +++++++ .../G1050/faulty/m3_tailor_pricer.clas.abap | 126 +++++++ .../G1050/faulty/m4_tailor_pricer.clas.abap | 126 +++++++ tasks_gen/train/G1050/generation.json | 44 +++ .../hidden/tailor_pricer_hidden.clas.abap | 131 +++++++ tasks_gen/train/G1050/mutation.json | 69 ++++ .../G1050/reference/tailor_pricer.clas.abap | 126 +++++++ .../reference/tailor_pricer.testclasses.abap | 70 ++++ .../train/G1050/seed/tailor_pricer.clas.abap | 104 ++++++ tasks_gen/train/G1050/spec.md | 47 +++ tasks_gen/train/G1050/task.json | 48 +++ .../G1051/faulty/m1_i_melt_out.ddls.asddls | 21 ++ .../G1051/faulty/m2_i_melt_out.ddls.asddls | 21 ++ .../G1051/faulty/m3_i_melt_out.ddls.asddls | 21 ++ .../G1051/faulty/m4_i_melt_out.ddls.asddls | 21 ++ tasks_gen/train/G1051/generation.json | 29 ++ .../train/G1051/hidden/melt_hidden.clas.abap | 115 +++++++ tasks_gen/train/G1051/mutation.json | 70 ++++ .../G1051/reference/i_melt_out.ddls.asddls | 21 ++ .../train/G1051/reference/melt_test.clas.abap | 53 +++ .../train/G1051/seed/casting.tabl.asabap | 14 + .../train/G1051/seed/i_melt_out.ddls.asddls | 18 + tasks_gen/train/G1051/seed/melt.tabl.asabap | 13 + tasks_gen/train/G1051/spec.md | 51 +++ tasks_gen/train/G1051/task.json | 73 ++++ .../G1053/faulty/m0_rack_billing.clas.abap | 116 +++++++ .../G1053/faulty/m1_rack_billing.clas.abap | 116 +++++++ .../G1053/faulty/m2_rack_billing.clas.abap | 116 +++++++ .../G1053/faulty/m3_rack_billing.clas.abap | 116 +++++++ .../G1053/faulty/m4_rack_billing.clas.abap | 116 +++++++ tasks_gen/train/G1053/generation.json | 29 ++ .../train/G1053/hidden/t02_hidden.clas.abap | 157 +++++++++ tasks_gen/train/G1053/mutation.json | 93 +++++ .../G1053/reference/rack_billing.clas.abap | 116 +++++++ .../reference/rack_billing.testclasses.abap | 88 +++++ tasks_gen/train/G1053/spec.md | 76 ++++ tasks_gen/train/G1053/task.json | 42 +++ .../train/G1054/faulty/m0_pest_fee.clas.abap | 105 ++++++ .../train/G1054/faulty/m1_pest_fee.clas.abap | 105 ++++++ .../train/G1054/faulty/m2_pest_fee.clas.abap | 105 ++++++ .../train/G1054/faulty/m3_pest_fee.clas.abap | 105 ++++++ .../train/G1054/faulty/m4_pest_fee.clas.abap | 105 ++++++ tasks_gen/train/G1054/generation.json | 24 ++ .../G1054/hidden/pest_test_hidden.clas.abap | 141 ++++++++ tasks_gen/train/G1054/mutation.json | 85 +++++ .../train/G1054/reference/pest_fee.clas.abap | 105 ++++++ .../G1054/reference/pest_fee.testclasses.abap | 73 ++++ tasks_gen/train/G1054/seed/pest_fee.clas.abap | 87 +++++ tasks_gen/train/G1054/spec.md | 51 +++ tasks_gen/train/G1054/task.json | 48 +++ .../G1056/faulty/m0_tyre_checker.clas.abap | 72 ++++ .../G1056/faulty/m1_tyre_checker.clas.abap | 72 ++++ .../G1056/faulty/m2_tyre_checker.clas.abap | 72 ++++ .../G1056/faulty/m3_tyre_checker.clas.abap | 72 ++++ .../G1056/faulty/m4_tyre_checker.clas.abap | 72 ++++ tasks_gen/train/G1056/generation.json | 29 ++ .../train/G1056/hidden/t20_hidden.clas.abap | 182 ++++++++++ tasks_gen/train/G1056/mutation.json | 76 ++++ .../G1056/reference/cx_tyre_error.clas.abap | 19 + .../G1056/reference/cx_tyre_load.clas.abap | 16 + .../G1056/reference/cx_tyre_qty.clas.abap | 16 + .../G1056/reference/cx_tyre_size.clas.abap | 16 + .../G1056/reference/cx_tyre_speed.clas.abap | 16 + .../G1056/reference/tyre_checker.clas.abap | 72 ++++ .../reference/tyre_checker.testclasses.abap | 96 ++++++ .../train/G1056/seed/if_tyre_order.intf.abap | 18 + tasks_gen/train/G1056/spec.md | 77 +++++ tasks_gen/train/G1056/task.json | 100 ++++++ .../faulty/m0_air_freight_pricer.clas.abap | 110 ++++++ .../faulty/m1_air_freight_pricer.clas.abap | 110 ++++++ .../faulty/m2_air_freight_pricer.clas.abap | 110 ++++++ .../faulty/m3_air_freight_pricer.clas.abap | 110 ++++++ .../faulty/m4_air_freight_pricer.clas.abap | 110 ++++++ tasks_gen/train/G1057/generation.json | 34 ++ .../G1057/hidden/air_frt_hidden.clas.abap | 179 ++++++++++ tasks_gen/train/G1057/mutation.json | 84 +++++ .../reference/air_freight_pricer.clas.abap | 110 ++++++ .../air_freight_pricer.testclasses.abap | 118 +++++++ .../train/G1057/seed/if_air_freight.intf.abap | 42 +++ tasks_gen/train/G1057/spec.md | 66 ++++ tasks_gen/train/G1057/task.json | 51 +++ .../train/G1058/faulty/m0_ice_fee.func.abap | 61 ++++ .../train/G1058/faulty/m2_ice_fee.func.abap | 61 ++++ .../train/G1058/faulty/m3_ice_fee.func.abap | 61 ++++ .../train/G1058/faulty/m4_ice_fee.func.abap | 61 ++++ tasks_gen/train/G1058/generation.json | 19 + .../train/G1058/hidden/t37_hidden.clas.abap | 127 +++++++ tasks_gen/train/G1058/mutation.json | 77 +++++ .../train/G1058/reference/ice_fee.func.abap | 61 ++++ .../train/G1058/reference/t37_test.clas.abap | 135 ++++++++ .../train/G1058/seed/ice_sample.func.abap | 18 + tasks_gen/train/G1058/spec.md | 55 +++ tasks_gen/train/G1058/task.json | 84 +++++ tasks_gen/train/G1059/generation.json | 14 + .../G1059/reference/sauna_pricer.clas.abap | 43 +++ .../reference/sauna_pricer.testclasses.abap | 54 +++ .../train/G1059/seed/if_sauna_price.intf.abap | 8 + tasks_gen/train/G1059/spec.md | 42 +++ tasks_gen/train/G1059/task.json | 61 ++++ .../G1060/faulty/m0_i_air_quality.ddls.asddls | 23 ++ .../G1060/faulty/m3_i_air_quality.ddls.asddls | 23 ++ .../G1060/faulty/m4_i_air_quality.ddls.asddls | 23 ++ tasks_gen/train/G1060/generation.json | 38 ++ .../train/G1060/hidden/t41_hidden.clas.abap | 112 ++++++ tasks_gen/train/G1060/mutation.json | 51 +++ .../G1060/reference/i_air_quality.ddls.asddls | 23 ++ .../train/G1060/reference/t41_test.clas.abap | 41 +++ .../train/G1060/seed/reading.tabl.asabap | 11 + .../train/G1060/seed/station.tabl.asabap | 11 + tasks_gen/train/G1060/spec.md | 44 +++ tasks_gen/train/G1060/task.json | 68 ++++ .../faulty/m0_watch_order_pool.clas.abap | 79 +++++ .../faulty/m1_watch_order_pool.clas.abap | 79 +++++ .../faulty/m2_watch_order_pool.clas.abap | 79 +++++ tasks_gen/train/G1061/generation.json | 24 ++ .../G1061/hidden/watch_order_hidden.clas.abap | 142 ++++++++ tasks_gen/train/G1061/mutation.json | 46 +++ .../reference/watch_order_pool.clas.abap | 79 +++++ .../watch_order_pool.testclasses.abap | 63 ++++ .../train/G1061/seed/if_watch_order.intf.abap | 38 ++ tasks_gen/train/G1061/spec.md | 77 +++++ tasks_gen/train/G1061/task.json | 51 +++ .../G1062/faulty/m0_parking_pricer.clas.abap | 112 ++++++ .../G1062/faulty/m1_parking_pricer.clas.abap | 112 ++++++ .../G1062/faulty/m2_parking_pricer.clas.abap | 112 ++++++ .../G1062/faulty/m3_parking_pricer.clas.abap | 112 ++++++ .../G1062/faulty/m4_parking_pricer.clas.abap | 112 ++++++ tasks_gen/train/G1062/generation.json | 19 + .../G1062/hidden/parking_hidden.clas.abap | 123 +++++++ tasks_gen/train/G1062/mutation.json | 80 +++++ .../G1062/reference/parking_pricer.clas.abap | 112 ++++++ .../reference/parking_pricer.testclasses.abap | 72 ++++ .../train/G1062/seed/parking_pricer.clas.abap | 91 +++++ tasks_gen/train/G1062/spec.md | 50 +++ tasks_gen/train/G1062/task.json | 52 +++ .../G1063/faulty/m2_toll_pricer.clas.abap | 103 ++++++ .../G1063/faulty/m3_toll_pricer.clas.abap | 103 ++++++ .../G1063/faulty/m4_toll_pricer.clas.abap | 103 ++++++ tasks_gen/train/G1063/generation.json | 19 + .../train/G1063/hidden/t02_hidden.clas.abap | 111 ++++++ tasks_gen/train/G1063/mutation.json | 63 ++++ .../G1063/reference/toll_pricer.clas.abap | 103 ++++++ .../reference/toll_pricer.testclasses.abap | 56 +++ .../train/G1063/seed/if_toll_pricer.intf.abap | 38 ++ tasks_gen/train/G1063/spec.md | 49 +++ tasks_gen/train/G1063/task.json | 51 +++ .../faulty/m1_care_visit_checker.clas.abap | 133 +++++++ .../faulty/m2_care_visit_checker.clas.abap | 133 +++++++ .../faulty/m3_care_visit_checker.clas.abap | 133 +++++++ .../faulty/m4_care_visit_checker.clas.abap | 133 +++++++ tasks_gen/train/G1064/generation.json | 34 ++ .../train/G1064/hidden/d01_hidden.clas.abap | 161 +++++++++ tasks_gen/train/G1064/mutation.json | 64 ++++ .../reference/care_visit_checker.clas.abap | 133 +++++++ .../care_visit_checker.testclasses.abap | 114 ++++++ .../train/G1064/seed/cx_care_visit.clas.abap | 22 ++ .../train/G1064/seed/if_care_reason.intf.abap | 15 + tasks_gen/train/G1064/spec.md | 77 +++++ tasks_gen/train/G1064/task.json | 60 ++++ .../faulty/m0_boat_hire_pricer.clas.abap | 103 ++++++ .../faulty/m1_boat_hire_pricer.clas.abap | 103 ++++++ .../faulty/m2_boat_hire_pricer.clas.abap | 103 ++++++ .../faulty/m4_boat_hire_pricer.clas.abap | 103 ++++++ tasks_gen/train/G1065/generation.json | 19 + .../train/G1065/hidden/t02_hidden.clas.abap | 196 +++++++++++ tasks_gen/train/G1065/mutation.json | 66 ++++ .../reference/boat_hire_pricer.clas.abap | 103 ++++++ .../boat_hire_pricer.testclasses.abap | 90 +++++ .../train/G1065/seed/hire_rates.clas.abap | 40 +++ tasks_gen/train/G1065/spec.md | 60 ++++ tasks_gen/train/G1065/task.json | 50 +++ .../train/G1066/faulty/m0_lib_fee.func.abap | 43 +++ .../train/G1066/faulty/m1_lib_fee.func.abap | 43 +++ .../train/G1066/faulty/m2_lib_fee.func.abap | 43 +++ .../train/G1066/faulty/m3_lib_fee.func.abap | 43 +++ tasks_gen/train/G1066/generation.json | 19 + .../train/G1066/hidden/t41_hidden.clas.abap | 135 ++++++++ tasks_gen/train/G1066/mutation.json | 64 ++++ .../train/G1066/reference/lib_fee.func.abap | 43 +++ .../train/G1066/reference/t41_test.clas.abap | 81 +++++ .../train/G1066/seed/lib_sample.func.abap | 15 + tasks_gen/train/G1066/spec.md | 47 +++ tasks_gen/train/G1066/task.json | 84 +++++ tasks_gen/train/G1067/generation.json | 31 ++ .../G1067/reference/tent_hire_fee.clas.abap | 69 ++++ .../reference/tent_hire_fee.testclasses.abap | 51 +++ .../train/G1067/seed/if_tent_hire.intf.abap | 25 ++ tasks_gen/train/G1067/spec.md | 49 +++ tasks_gen/train/G1067/task.json | 64 ++++ tasks_gen/train/G1068/generation.json | 44 +++ .../train/G1068/hidden/t16_hidden.clas.abap | 89 +++++ .../train/G1068/reference/i_hotel.ddls.asddls | 9 + .../train/G1068/reference/i_stay.ddls.asddls | 16 + .../train/G1068/reference/t16_test.clas.abap | 42 +++ tasks_gen/train/G1068/seed/hotel.tabl.asabap | 11 + .../train/G1068/seed/seed_data.clas.abap | 37 ++ tasks_gen/train/G1068/seed/stay.tabl.asabap | 13 + tasks_gen/train/G1068/spec.md | 46 +++ tasks_gen/train/G1068/task.json | 90 +++++ .../G1069/faulty/m0_league_table.clas.abap | 123 +++++++ .../G1069/faulty/m1_league_table.clas.abap | 123 +++++++ .../G1069/faulty/m2_league_table.clas.abap | 123 +++++++ .../G1069/faulty/m3_league_table.clas.abap | 123 +++++++ .../G1069/faulty/m4_league_table.clas.abap | 123 +++++++ tasks_gen/train/G1069/generation.json | 19 + .../train/G1069/hidden/t31_hidden.clas.abap | 142 ++++++++ tasks_gen/train/G1069/mutation.json | 65 ++++ .../G1069/reference/league_table.clas.abap | 123 +++++++ .../reference/league_table.testclasses.abap | 47 +++ tasks_gen/train/G1069/spec.md | 67 ++++ tasks_gen/train/G1069/task.json | 44 +++ .../G1070/faulty/m0_shred_pricer.clas.abap | 152 ++++++++ .../G1070/faulty/m1_shred_pricer.clas.abap | 152 ++++++++ .../G1070/faulty/m2_shred_pricer.clas.abap | 152 ++++++++ .../G1070/faulty/m3_shred_pricer.clas.abap | 152 ++++++++ .../G1070/faulty/m4_shred_pricer.clas.abap | 152 ++++++++ tasks_gen/train/G1070/generation.json | 19 + .../train/G1070/hidden/shred_hidden.clas.abap | 107 ++++++ tasks_gen/train/G1070/mutation.json | 83 +++++ .../G1070/reference/shred_pricer.clas.abap | 152 ++++++++ .../reference/shred_pricer.testclasses.abap | 71 ++++ .../train/G1070/seed/shred_pricer.clas.abap | 107 ++++++ tasks_gen/train/G1070/spec.md | 52 +++ tasks_gen/train/G1070/task.json | 48 +++ .../G1071/faulty/m0_i_dive_stats.ddls.asddls | 15 + .../G1071/faulty/m1_i_dive_stats.ddls.asddls | 15 + .../G1071/faulty/m2_i_dive_stats.ddls.asddls | 15 + tasks_gen/train/G1071/generation.json | 40 +++ .../hidden/t_dive_stats_hidden.clas.abap | 78 +++++ tasks_gen/train/G1071/mutation.json | 42 +++ .../G1071/reference/i_dive_stats.ddls.asddls | 15 + .../reference/t_dive_stats_test.clas.abap | 41 +++ tasks_gen/train/G1071/seed/dive.tabl.asabap | 12 + tasks_gen/train/G1071/seed/diver.tabl.asabap | 10 + .../train/G1071/seed/i_dive_stats.ddls.asddls | 19 + .../train/G1071/seed/seed_dive.clas.abap | 34 ++ tasks_gen/train/G1071/seed/site.tabl.asabap | 11 + tasks_gen/train/G1071/spec.md | 47 +++ tasks_gen/train/G1071/task.json | 86 +++++ .../G1072/faulty/m1_i_guest_stay.ddls.asddls | 17 + .../G1072/faulty/m2_i_guest_stay.ddls.asddls | 17 + .../G1072/faulty/m3_i_guest_stay.ddls.asddls | 17 + .../G1072/faulty/m4_i_guest_stay.ddls.asddls | 17 + tasks_gen/train/G1072/generation.json | 19 + .../train/G1072/hidden/t30_hidden.clas.abap | 89 +++++ tasks_gen/train/G1072/mutation.json | 60 ++++ .../G1072/reference/i_guest_stay.ddls.asddls | 17 + .../train/G1072/reference/t30_test.clas.abap | 45 +++ tasks_gen/train/G1072/seed/guest.tabl.asabap | 10 + tasks_gen/train/G1072/seed/stay.tabl.asabap | 13 + tasks_gen/train/G1072/spec.md | 39 +++ tasks_gen/train/G1072/task.json | 66 ++++ tasks_gen/train/G1073/generation.json | 34 ++ .../train/G1073/hidden/t31_hidden.clas.abap | 151 ++++++++ .../G1073/reference/parking_eval.clas.abap | 96 ++++++ .../reference/parking_eval.testclasses.abap | 75 ++++ .../train/G1073/seed/if_park_eval.intf.abap | 22 ++ tasks_gen/train/G1073/spec.md | 57 +++ tasks_gen/train/G1073/task.json | 52 +++ .../faulty/m0_pallet_allocator.clas.abap | 108 ++++++ .../faulty/m1_pallet_allocator.clas.abap | 108 ++++++ .../faulty/m2_pallet_allocator.clas.abap | 108 ++++++ .../faulty/m3_pallet_allocator.clas.abap | 108 ++++++ .../faulty/m4_pallet_allocator.clas.abap | 108 ++++++ tasks_gen/train/G1074/generation.json | 31 ++ .../G1074/hidden/pallet_hidden.clas.abap | 121 +++++++ tasks_gen/train/G1074/mutation.json | 72 ++++ .../reference/pallet_allocator.clas.abap | 108 ++++++ .../pallet_allocator.testclasses.abap | 67 ++++ .../G1074/seed/pallet_allocator.clas.abap | 96 ++++++ tasks_gen/train/G1074/spec.md | 59 ++++ tasks_gen/train/G1074/task.json | 48 +++ .../faulty/m0_childcare_pricer.clas.abap | 132 +++++++ .../faulty/m1_childcare_pricer.clas.abap | 132 +++++++ .../faulty/m2_childcare_pricer.clas.abap | 132 +++++++ .../faulty/m3_childcare_pricer.clas.abap | 132 +++++++ tasks_gen/train/G1075/generation.json | 24 ++ .../train/G1075/hidden/t02_hidden.clas.abap | 196 +++++++++++ tasks_gen/train/G1075/mutation.json | 87 +++++ .../reference/childcare_pricer.clas.abap | 132 +++++++ .../childcare_pricer.testclasses.abap | 85 +++++ tasks_gen/train/G1075/spec.md | 65 ++++ tasks_gen/train/G1075/task.json | 41 +++ .../G1076/faulty/m0_claim_checker.clas.abap | 74 ++++ .../G1076/faulty/m3_claim_checker.clas.abap | 74 ++++ .../G1076/faulty/m4_claim_checker.clas.abap | 74 ++++ tasks_gen/train/G1076/generation.json | 34 ++ .../G1076/hidden/claim_check_hidden.clas.abap | 158 +++++++++ tasks_gen/train/G1076/mutation.json | 57 +++ .../G1076/reference/claim_checker.clas.abap | 74 ++++ .../reference/claim_checker.testclasses.abap | 132 +++++++ .../train/G1076/seed/cx_claim_error.clas.abap | 24 ++ tasks_gen/train/G1076/spec.md | 58 ++++ tasks_gen/train/G1076/task.json | 50 +++ .../G1077/faulty/m0_water_bill.clas.abap | 87 +++++ .../G1077/faulty/m1_water_bill.clas.abap | 87 +++++ .../G1077/faulty/m2_water_bill.clas.abap | 87 +++++ .../G1077/faulty/m3_water_bill.clas.abap | 87 +++++ .../G1077/faulty/m4_water_bill.clas.abap | 87 +++++ tasks_gen/train/G1077/generation.json | 19 + .../G1077/hidden/water_bill_test.clas.abap | 76 ++++ tasks_gen/train/G1077/mutation.json | 86 +++++ .../G1077/reference/water_bill.clas.abap | 87 +++++ .../reference/water_bill.testclasses.abap | 51 +++ .../train/G1077/seed/water_tariff.clas.abap | 44 +++ tasks_gen/train/G1077/spec.md | 62 ++++ tasks_gen/train/G1077/task.json | 50 +++ .../train/G1078/faulty/m0_sewer_fee.func.abap | 51 +++ .../train/G1078/faulty/m1_sewer_fee.func.abap | 51 +++ .../train/G1078/faulty/m2_sewer_fee.func.abap | 51 +++ .../train/G1078/faulty/m3_sewer_fee.func.abap | 51 +++ .../train/G1078/faulty/m4_sewer_fee.func.abap | 51 +++ tasks_gen/train/G1078/generation.json | 30 ++ .../train/G1078/hidden/sewer_hidden.clas.abap | 97 ++++++ tasks_gen/train/G1078/mutation.json | 66 ++++ .../train/G1078/reference/sewer_fee.func.abap | 51 +++ .../G1078/reference/sewer_test.clas.abap | 93 +++++ .../train/G1078/seed/sewer_sample.func.abap | 15 + tasks_gen/train/G1078/spec.md | 54 +++ tasks_gen/train/G1078/task.json | 85 +++++ tasks_gen/train/G1079/generation.json | 23 ++ .../G1079/reference/soil_evaluator.clas.abap | 106 ++++++ .../reference/soil_evaluator.testclasses.abap | 60 ++++ tasks_gen/train/G1079/spec.md | 50 +++ tasks_gen/train/G1079/task.json | 55 +++ .../G1080/faulty/m0_i_vend_state.ddls.asddls | 25 ++ .../G1080/faulty/m1_i_vend_state.ddls.asddls | 25 ++ .../G1080/faulty/m2_i_vend_state.ddls.asddls | 25 ++ .../G1080/faulty/m3_i_vend_state.ddls.asddls | 25 ++ .../G1080/faulty/m4_i_vend_state.ddls.asddls | 25 ++ tasks_gen/train/G1080/generation.json | 24 ++ .../train/G1080/hidden/t16_hidden.clas.abap | 109 ++++++ tasks_gen/train/G1080/mutation.json | 59 ++++ .../G1080/reference/i_vend_state.ddls.asddls | 25 ++ .../train/G1080/reference/t16_test.clas.abap | 56 +++ tasks_gen/train/G1080/seed/vmach.tabl.asabap | 10 + tasks_gen/train/G1080/seed/vslot.tabl.asabap | 13 + tasks_gen/train/G1080/spec.md | 44 +++ tasks_gen/train/G1080/task.json | 67 ++++ .../G1081/faulty/m0_studio_summary.clas.abap | 64 ++++ .../G1081/faulty/m1_studio_summary.clas.abap | 64 ++++ .../G1081/faulty/m2_studio_summary.clas.abap | 64 ++++ .../G1081/faulty/m3_studio_summary.clas.abap | 64 ++++ .../G1081/faulty/m4_studio_summary.clas.abap | 64 ++++ tasks_gen/train/G1081/generation.json | 24 ++ .../train/G1081/hidden/t30_hidden.clas.abap | 139 ++++++++ tasks_gen/train/G1081/mutation.json | 74 ++++ .../G1081/reference/studio_summary.clas.abap | 64 ++++ .../reference/studio_summary.testclasses.abap | 71 ++++ tasks_gen/train/G1081/spec.md | 55 +++ tasks_gen/train/G1081/task.json | 42 +++ .../G1082/faulty/m0_climb_pricer.clas.abap | 137 ++++++++ .../G1082/faulty/m1_climb_pricer.clas.abap | 137 ++++++++ .../G1082/faulty/m2_climb_pricer.clas.abap | 137 ++++++++ .../G1082/faulty/m3_climb_pricer.clas.abap | 137 ++++++++ .../G1082/faulty/m4_climb_pricer.clas.abap | 137 ++++++++ tasks_gen/train/G1082/generation.json | 37 ++ .../train/G1082/hidden/climb_hidden.clas.abap | 136 ++++++++ tasks_gen/train/G1082/mutation.json | 79 +++++ .../G1082/reference/climb_pricer.clas.abap | 137 ++++++++ .../reference/climb_pricer.testclasses.abap | 88 +++++ .../train/G1082/seed/climb_pricer.clas.abap | 71 ++++ tasks_gen/train/G1082/spec.md | 53 +++ tasks_gen/train/G1082/task.json | 48 +++ .../train/G1083/faulty/m0_glaz_calc.clas.abap | 54 +++ .../train/G1083/faulty/m1_glaz_calc.clas.abap | 54 +++ .../train/G1083/faulty/m2_glaz_calc.clas.abap | 54 +++ .../train/G1083/faulty/m3_glaz_calc.clas.abap | 54 +++ .../train/G1083/faulty/m4_glaz_calc.clas.abap | 54 +++ tasks_gen/train/G1083/generation.json | 24 ++ .../train/G1083/hidden/t31_hidden.clas.abap | 129 +++++++ tasks_gen/train/G1083/mutation.json | 73 ++++ .../train/G1083/reference/glaz_calc.clas.abap | 54 +++ .../reference/glaz_calc.clas.testclasses.abap | 73 ++++ .../train/G1083/reference/glaz_fee.func.abap | 25 ++ .../train/G1083/reference/t31_test.clas.abap | 68 ++++ .../train/G1083/seed/glaz_sample.func.abap | 14 + tasks_gen/train/G1083/spec.md | 74 ++++ tasks_gen/train/G1083/task.json | 96 ++++++ .../faulty/m0_seed_lot_checker.clas.abap | 75 ++++ .../faulty/m1_seed_lot_checker.clas.abap | 75 ++++ .../faulty/m2_seed_lot_checker.clas.abap | 75 ++++ .../faulty/m3_seed_lot_checker.clas.abap | 75 ++++ .../faulty/m4_seed_lot_checker.clas.abap | 75 ++++ tasks_gen/train/G1084/generation.json | 24 ++ .../G1084/hidden/seed_lot_hidden.clas.abap | 210 +++++++++++ tasks_gen/train/G1084/mutation.json | 71 ++++ .../reference/seed_lot_checker.clas.abap | 75 ++++ .../seed_lot_checker.testclasses.abap | 109 ++++++ .../train/G1084/seed/cx_lot_date.clas.abap | 13 + .../train/G1084/seed/cx_lot_format.clas.abap | 13 + .../G1084/seed/cx_lot_germination.clas.abap | 13 + .../G1084/seed/cx_lot_moisture.clas.abap | 13 + .../train/G1084/seed/cx_seed_lot.clas.abap | 21 ++ tasks_gen/train/G1084/spec.md | 60 ++++ tasks_gen/train/G1084/task.json | 78 +++++ .../train/G1085/faulty/m0_dlv_price.func.abap | 39 +++ .../train/G1085/faulty/m1_dlv_price.func.abap | 39 +++ .../train/G1085/faulty/m2_dlv_price.func.abap | 39 +++ .../train/G1085/faulty/m3_dlv_price.func.abap | 39 +++ .../train/G1085/faulty/m4_dlv_price.func.abap | 39 +++ tasks_gen/train/G1085/generation.json | 19 + .../train/G1085/hidden/t14_hidden.clas.abap | 83 +++++ tasks_gen/train/G1085/mutation.json | 57 +++ .../train/G1085/reference/dlv_price.func.abap | 39 +++ .../train/G1085/reference/t14_test.clas.abap | 55 +++ .../train/G1085/seed/dlv_sample.func.abap | 18 + tasks_gen/train/G1085/spec.md | 45 +++ tasks_gen/train/G1085/task.json | 84 +++++ .../train/G1086/faulty/m0_wood_fee.func.abap | 55 +++ .../train/G1086/faulty/m1_wood_fee.func.abap | 55 +++ .../train/G1086/faulty/m2_wood_fee.func.abap | 55 +++ .../train/G1086/faulty/m3_wood_fee.func.abap | 55 +++ .../train/G1086/faulty/m4_wood_fee.func.abap | 55 +++ tasks_gen/train/G1086/generation.json | 19 + .../train/G1086/hidden/wood_hidden.clas.abap | 132 +++++++ tasks_gen/train/G1086/mutation.json | 70 ++++ .../train/G1086/reference/wood_fee.func.abap | 55 +++ .../train/G1086/reference/wood_test.clas.abap | 88 +++++ .../train/G1086/seed/wood_sample.func.abap | 16 + tasks_gen/train/G1086/spec.md | 45 +++ tasks_gen/train/G1086/task.json | 84 +++++ tasks_gen/train/G1087/generation.json | 14 + .../train/G1087/reference/tool_hire.clas.abap | 62 ++++ .../reference/tool_hire.testclasses.abap | 67 ++++ .../train/G1087/seed/if_tool_hire.intf.abap | 21 ++ tasks_gen/train/G1087/spec.md | 52 +++ tasks_gen/train/G1087/task.json | 61 ++++ .../G1089/faulty/m1_keg_tour_merger.clas.abap | 96 ++++++ .../G1089/faulty/m2_keg_tour_merger.clas.abap | 96 ++++++ .../G1089/faulty/m3_keg_tour_merger.clas.abap | 96 ++++++ .../G1089/faulty/m4_keg_tour_merger.clas.abap | 96 ++++++ tasks_gen/train/G1089/generation.json | 19 + .../train/G1089/hidden/keg_hidden.clas.abap | 162 +++++++++ tasks_gen/train/G1089/mutation.json | 62 ++++ .../G1089/reference/keg_tour_merger.clas.abap | 96 ++++++ .../keg_tour_merger.testclasses.abap | 87 +++++ tasks_gen/train/G1089/spec.md | 63 ++++ tasks_gen/train/G1089/task.json | 47 +++ .../G1090/faulty/m1_ski_lift_old.prog.abap | 97 ++++++ .../G1090/faulty/m2_ski_lift_old.prog.abap | 97 ++++++ tasks_gen/train/G1090/generation.json | 19 + .../train/G1090/hidden/ski_hidden.clas.abap | 97 ++++++ tasks_gen/train/G1090/mutation.json | 44 +++ .../G1090/reference/ski_lift_old.prog.abap | 97 ++++++ .../train/G1090/seed/ski_lift_old.prog.abap | 78 +++++ tasks_gen/train/G1090/seed/ski_seed.clas.abap | 18 + .../train/G1090/seed/skiride.tabl.asabap | 13 + tasks_gen/train/G1090/spec.md | 48 +++ tasks_gen/train/G1090/task.json | 67 ++++ .../G1091/faulty/m0_i_po_value.ddls.asddls | 17 + .../G1091/faulty/m1_i_po_value.ddls.asddls | 17 + .../G1091/faulty/m2_i_po_value.ddls.asddls | 17 + .../G1091/faulty/m3_i_po_value.ddls.asddls | 17 + .../G1091/faulty/m4_i_po_value.ddls.asddls | 17 + tasks_gen/train/G1091/generation.json | 19 + .../train/G1091/hidden/tpo_hidden.clas.abap | 95 +++++ tasks_gen/train/G1091/mutation.json | 66 ++++ .../G1091/reference/i_po_value.ddls.asddls | 17 + .../train/G1091/reference/tpo_test.clas.abap | 53 +++ .../train/G1091/seed/i_po_value.ddls.asddls | 16 + .../train/G1091/seed/po_head.tabl.asabap | 12 + .../train/G1091/seed/po_item.tabl.asabap | 13 + tasks_gen/train/G1091/spec.md | 45 +++ tasks_gen/train/G1091/task.json | 72 ++++ .../G1092/faulty/m0_i_route_load.ddls.asddls | 15 + .../G1092/faulty/m1_i_route_load.ddls.asddls | 15 + .../G1092/faulty/m2_i_route_load.ddls.asddls | 15 + .../G1092/faulty/m3_i_route_load.ddls.asddls | 15 + .../G1092/faulty/m4_i_route_load.ddls.asddls | 15 + tasks_gen/train/G1092/generation.json | 38 ++ .../train/G1092/hidden/t16_hidden.clas.abap | 125 +++++++ tasks_gen/train/G1092/mutation.json | 67 ++++ .../G1092/reference/i_route_load.ddls.asddls | 15 + .../train/G1092/reference/t16_test.clas.abap | 59 ++++ tasks_gen/train/G1092/seed/sail.tabl.asabap | 11 + tasks_gen/train/G1092/seed/veh.tabl.asabap | 13 + tasks_gen/train/G1092/spec.md | 41 +++ tasks_gen/train/G1092/task.json | 65 ++++ tasks_gen/train/G1093/generation.json | 42 +++ .../train/G1093/hidden/mix_hidden.clas.abap | 121 +++++++ tasks_gen/train/G1093/mutation.json | 65 ++++ .../train/G1093/reference/mix_price.func.abap | 57 +++ .../train/G1093/reference/mix_test.clas.abap | 73 ++++ .../train/G1093/seed/mix_sample.func.abap | 17 + .../train/G1093/seed/paint_mix.clas.abap | 28 ++ tasks_gen/train/G1093/seed/tintpr.tabl.abap | 10 + tasks_gen/train/G1093/spec.md | 73 ++++ tasks_gen/train/G1093/task.json | 91 +++++ tasks_gen/train/G1094/generation.json | 50 +++ .../train/G1094/hidden/court_hidden.clas.abap | 125 +++++++ tasks_gen/train/G1094/mutation.json | 34 ++ .../train/G1094/reference/court_old.prog.abap | 118 +++++++ .../train/G1094/seed/booking.tabl.asabap | 14 + tasks_gen/train/G1094/seed/court.tabl.asabap | 12 + .../train/G1094/seed/court_old.prog.abap | 92 +++++ .../train/G1094/seed/court_seed.clas.abap | 32 ++ tasks_gen/train/G1094/spec.md | 44 +++ tasks_gen/train/G1094/task.json | 73 ++++ .../G1095/faulty/m0_heli_pricer.clas.abap | 113 ++++++ .../train/G1095/faulty/m1_heli_fee.func.abap | 43 +++ .../G1095/faulty/m2_heli_pricer.clas.abap | 113 ++++++ .../train/G1095/faulty/m3_heli_fee.func.abap | 43 +++ .../G1095/faulty/m4_heli_pricer.clas.abap | 113 ++++++ tasks_gen/train/G1095/generation.json | 24 ++ .../train/G1095/hidden/t14_hidden.clas.abap | 128 +++++++ tasks_gen/train/G1095/mutation.json | 73 ++++ .../G1095/reference/cx_heli_invalid.clas.abap | 27 ++ .../train/G1095/reference/heli_fee.func.abap | 43 +++ .../G1095/reference/heli_pricer.clas.abap | 113 ++++++ .../heli_pricer_testclasses.clas.abap | 97 ++++++ .../train/G1095/reference/t14_test.clas.abap | 85 +++++ tasks_gen/train/G1095/seed/heli_fee.func.abap | 17 + .../train/G1095/seed/heli_sample.func.abap | 17 + tasks_gen/train/G1095/spec.md | 53 +++ tasks_gen/train/G1095/task.json | 108 ++++++ .../G1096/faulty/m0_adopt_review.func.abap | 49 +++ .../G1096/faulty/m1_adopt_review.func.abap | 49 +++ .../G1096/faulty/m2_adopt_review.func.abap | 49 +++ .../G1096/faulty/m3_adopt_review.func.abap | 49 +++ .../G1096/faulty/m4_adopt_review.func.abap | 49 +++ tasks_gen/train/G1096/generation.json | 28 ++ .../train/G1096/hidden/adopt_hidden.clas.abap | 131 +++++++ tasks_gen/train/G1096/mutation.json | 68 ++++ .../G1096/reference/adopt_review.func.abap | 49 +++ .../G1096/reference/adopt_test.clas.abap | 76 ++++ .../G1096/reference/cx_adopt_reject.clas.abap | 21 ++ .../train/G1096/seed/shelter_sample.func.abap | 12 + tasks_gen/train/G1096/spec.md | 65 ++++ tasks_gen/train/G1096/task.json | 104 ++++++ .../train/G1097/faulty/m0_weld_fee.func.abap | 53 +++ .../train/G1097/faulty/m1_weld_fee.func.abap | 53 +++ .../train/G1097/faulty/m2_weld_fee.func.abap | 53 +++ .../train/G1097/faulty/m4_weld_fee.func.abap | 53 +++ tasks_gen/train/G1097/generation.json | 19 + .../train/G1097/hidden/t14_hidden.clas.abap | 112 ++++++ tasks_gen/train/G1097/mutation.json | 71 ++++ .../train/G1097/reference/t14_test.clas.abap | 61 ++++ .../train/G1097/reference/weld_fee.func.abap | 53 +++ .../train/G1097/seed/weld_sample.func.abap | 15 + tasks_gen/train/G1097/spec.md | 48 +++ tasks_gen/train/G1097/task.json | 84 +++++ .../train/G1098/faulty/m0_drv_fee.func.abap | 56 +++ .../train/G1098/faulty/m1_drv_fee.func.abap | 56 +++ .../train/G1098/faulty/m2_drv_fee.func.abap | 56 +++ .../train/G1098/faulty/m3_drv_fee.func.abap | 56 +++ .../train/G1098/faulty/m4_drv_fee.func.abap | 56 +++ tasks_gen/train/G1098/generation.json | 19 + .../train/G1098/hidden/t41_hidden.clas.abap | 138 ++++++++ tasks_gen/train/G1098/mutation.json | 65 ++++ .../train/G1098/reference/drv_fee.func.abap | 56 +++ .../train/G1098/reference/t41_test.clas.abap | 89 +++++ .../train/G1098/seed/drv_sample.func.abap | 16 + tasks_gen/train/G1098/spec.md | 45 +++ tasks_gen/train/G1098/task.json | 84 +++++ tasks_gen/train/G1099/generation.json | 14 + .../G1099/reference/salon_pricer.clas.abap | 38 ++ .../reference/salon_pricer.testclasses.abap | 44 +++ .../train/G1099/seed/if_salon_price.intf.abap | 14 + tasks_gen/train/G1099/spec.md | 43 +++ tasks_gen/train/G1099/task.json | 60 ++++ .../G1100/faulty/m0_i_berth_fee.ddls.asddls | 22 ++ .../G1100/faulty/m1_i_berth_fee.ddls.asddls | 22 ++ .../G1100/faulty/m2_i_berth_fee.ddls.asddls | 22 ++ .../G1100/faulty/m3_i_berth_fee.ddls.asddls | 22 ++ .../G1100/faulty/m4_i_berth_fee.ddls.asddls | 22 ++ tasks_gen/train/G1100/generation.json | 37 ++ .../train/G1100/hidden/t42_hidden.clas.abap | 129 +++++++ tasks_gen/train/G1100/mutation.json | 62 ++++ .../G1100/reference/i_berth_fee.ddls.asddls | 22 ++ .../train/G1100/reference/t42_test.clas.abap | 45 +++ tasks_gen/train/G1100/seed/berth.tabl.asabap | 14 + tasks_gen/train/G1100/seed/pier.tabl.asabap | 12 + .../train/G1100/seed/seed_berth.clas.abap | 36 ++ tasks_gen/train/G1100/spec.md | 47 +++ tasks_gen/train/G1100/task.json | 73 ++++ tasks_gen/train/G1101/generation.json | 36 ++ .../train/G1101/hidden/t41_hidden.clas.abap | 212 ++++++++++++ tasks_gen/train/G1101/mutation.json | 77 +++++ .../G1101/reference/banquet_fee.func.abap | 57 +++ .../train/G1101/reference/t41_test.clas.abap | 120 +++++++ tasks_gen/train/G1101/seed/dish.tabl.abap | 11 + tasks_gen/train/G1101/seed/pos.tabl.abap | 12 + .../train/G1101/seed/seed_banquet.clas.abap | 36 ++ tasks_gen/train/G1101/spec.md | 61 ++++ tasks_gen/train/G1101/task.json | 97 ++++++ tasks_gen/train/G1102/generation.json | 46 +++ .../train/G1102/hidden/shop_hidden.clas.abap | 112 ++++++ .../G1102/reference/farm_shop_old.prog.abap | 172 +++++++++ .../train/G1102/seed/farm_shop_old.prog.abap | 83 +++++ tasks_gen/train/G1102/seed/gdsale.tabl.asabap | 13 + tasks_gen/train/G1102/seed/goods.tabl.asabap | 11 + .../train/G1102/seed/shop_seed.clas.abap | 31 ++ tasks_gen/train/G1102/spec.md | 47 +++ tasks_gen/train/G1102/task.json | 73 ++++ .../train/G1103/faulty/m0_wash_fee.func.abap | 59 ++++ .../train/G1103/faulty/m1_wash_fee.func.abap | 59 ++++ .../train/G1103/faulty/m2_wash_fee.func.abap | 59 ++++ .../train/G1103/faulty/m3_wash_fee.func.abap | 59 ++++ .../train/G1103/faulty/m4_wash_fee.func.abap | 59 ++++ tasks_gen/train/G1103/generation.json | 24 ++ .../train/G1103/hidden/t37_hidden.clas.abap | 100 ++++++ tasks_gen/train/G1103/mutation.json | 66 ++++ .../train/G1103/reference/t37_test.clas.abap | 101 ++++++ .../train/G1103/reference/wash_fee.func.abap | 59 ++++ .../train/G1103/seed/wash_sample.func.abap | 17 + tasks_gen/train/G1103/spec.md | 47 +++ tasks_gen/train/G1103/task.json | 88 +++++ .../train/G1104/faulty/m0_golf_fee.func.abap | 67 ++++ .../train/G1104/faulty/m1_golf_fee.func.abap | 67 ++++ .../train/G1104/faulty/m2_golf_fee.func.abap | 67 ++++ .../train/G1104/faulty/m3_golf_fee.func.abap | 67 ++++ .../train/G1104/faulty/m4_golf_fee.func.abap | 67 ++++ tasks_gen/train/G1104/generation.json | 34 ++ .../train/G1104/hidden/t71_hidden.clas.abap | 123 +++++++ tasks_gen/train/G1104/mutation.json | 77 +++++ .../train/G1104/reference/cx_golf.clas.abap | 24 ++ .../train/G1104/reference/golf_fee.func.abap | 67 ++++ .../train/G1104/reference/t71_test.clas.abap | 78 +++++ tasks_gen/train/G1104/spec.md | 73 ++++ tasks_gen/train/G1104/task.json | 86 +++++ .../G1105/faulty/m0_kennel_fee.func.abap | 43 +++ .../G1105/faulty/m1_kennel_fee.func.abap | 43 +++ .../G1105/faulty/m2_kennel_fee.func.abap | 43 +++ .../G1105/faulty/m3_kennel_fee.func.abap | 43 +++ .../G1105/faulty/m4_kennel_fee.func.abap | 43 +++ tasks_gen/train/G1105/generation.json | 19 + .../train/G1105/hidden/t42_hidden.clas.abap | 105 ++++++ tasks_gen/train/G1105/mutation.json | 69 ++++ .../G1105/reference/kennel_fee.func.abap | 43 +++ .../train/G1105/reference/t42_test.clas.abap | 61 ++++ .../train/G1105/seed/kennel_sample.func.abap | 16 + tasks_gen/train/G1105/spec.md | 43 +++ tasks_gen/train/G1105/task.json | 84 +++++ tasks_gen/train/G1106/generation.json | 37 ++ .../train/G1106/hidden/bake_hidden.clas.abap | 113 ++++++ .../G1106/reference/bake_sales.prog.abap | 173 ++++++++++ .../train/G1106/seed/bake_seed.clas.abap | 57 +++ tasks_gen/train/G1106/seed/deliv.tabl.asabap | 13 + tasks_gen/train/G1106/spec.md | 49 +++ tasks_gen/train/G1106/task.json | 60 ++++ tasks_gen/train/G1107/generation.json | 14 + .../train/G1107/reference/lkr_fee.func.abap | 34 ++ .../train/G1107/reference/lkr_test.clas.abap | 55 +++ .../train/G1107/seed/lkr_sample.func.abap | 17 + tasks_gen/train/G1107/spec.md | 43 +++ tasks_gen/train/G1107/task.json | 90 +++++ tasks_gen/train/G1108/generation.json | 50 +++ .../train/G1108/hidden/t31_hidden.clas.abap | 109 ++++++ .../reference/i_lesson_instr.ddls.asddls | 17 + .../train/G1108/reference/t31_test.clas.abap | 55 +++ tasks_gen/train/G1108/seed/instr.tabl.asabap | 11 + tasks_gen/train/G1108/seed/lesson.tabl.asabap | 13 + tasks_gen/train/G1108/spec.md | 43 +++ tasks_gen/train/G1108/task.json | 69 ++++ tasks_gen/train/G1109/generation.json | 40 +++ .../train/G1109/hidden/tel_hidden.clas.abap | 129 +++++++ .../train/G1109/reference/tel_rate.func.abap | 51 +++ .../train/G1109/reference/tel_test.clas.abap | 73 ++++ .../train/G1109/seed/tel_sample.func.abap | 16 + tasks_gen/train/G1109/spec.md | 63 ++++ tasks_gen/train/G1109/task.json | 86 +++++ .../train/G1110/faulty/m1_scrap_pay.prog.abap | 115 +++++++ .../train/G1110/faulty/m2_scrap_pay.prog.abap | 115 +++++++ .../train/G1110/faulty/m3_scrap_pay.prog.abap | 115 +++++++ .../train/G1110/faulty/m4_scrap_pay.prog.abap | 115 +++++++ tasks_gen/train/G1110/generation.json | 19 + .../train/G1110/hidden/scrap_hidden.clas.abap | 117 +++++++ tasks_gen/train/G1110/mutation.json | 73 ++++ .../train/G1110/reference/scrap_pay.prog.abap | 115 +++++++ tasks_gen/train/G1110/seed/scrap.tabl.asabap | 13 + .../train/G1110/seed/scrap_pay.prog.abap | 105 ++++++ .../train/G1110/seed/scrap_seed.clas.abap | 21 ++ tasks_gen/train/G1110/spec.md | 52 +++ tasks_gen/train/G1110/task.json | 66 ++++ .../G1111/faulty/m0_i_tour_load.ddls.asddls | 16 + .../G1111/faulty/m1_i_tour_load.ddls.asddls | 16 + .../G1111/faulty/m2_i_tour_load.ddls.asddls | 16 + tasks_gen/train/G1111/generation.json | 19 + .../train/G1111/hidden/t31_hidden.clas.abap | 98 ++++++ tasks_gen/train/G1111/mutation.json | 43 +++ .../G1111/reference/i_tour_load.ddls.asddls | 16 + .../train/G1111/reference/t31_test.clas.abap | 52 +++ .../train/G1111/seed/i_tour_load.ddls.asddls | 18 + tasks_gen/train/G1111/seed/parcel.tabl.asabap | 12 + tasks_gen/train/G1111/seed/tour.tabl.asabap | 12 + tasks_gen/train/G1111/spec.md | 43 +++ tasks_gen/train/G1111/task.json | 72 ++++ .../G1112/faulty/m0_i_blood_stock.ddls.asddls | 14 + .../G1112/faulty/m1_i_blood_stock.ddls.asddls | 14 + .../G1112/faulty/m2_i_blood_stock.ddls.asddls | 14 + .../G1112/faulty/m3_i_blood_stock.ddls.asddls | 14 + .../G1112/faulty/m4_i_blood_stock.ddls.asddls | 14 + tasks_gen/train/G1112/generation.json | 19 + .../train/G1112/hidden/blood_hidden.clas.abap | 95 +++++ tasks_gen/train/G1112/mutation.json | 72 ++++ .../G1112/reference/blood_test.clas.abap | 40 +++ .../G1112/reference/i_blood_stock.ddls.asddls | 14 + tasks_gen/train/G1112/seed/camp.tabl.asabap | 10 + tasks_gen/train/G1112/seed/donat.tabl.asabap | 12 + tasks_gen/train/G1112/spec.md | 40 +++ tasks_gen/train/G1112/task.json | 65 ++++ tasks_gen/train/G1113/generation.json | 29 ++ .../train/G1113/hidden/t14_hidden.clas.abap | 248 +++++++++++++ .../train/G1113/reference/don_sum.func.abap | 70 ++++ .../train/G1113/reference/t14_test.clas.abap | 120 +++++++ .../train/G1113/seed/don_sample.func.abap | 18 + tasks_gen/train/G1113/spec.md | 58 ++++ tasks_gen/train/G1113/task.json | 77 +++++ .../G1114/faulty/m0_bakery_bill_old.prog.abap | 181 ++++++++++ .../G1114/faulty/m1_bakery_bill_old.prog.abap | 181 ++++++++++ .../G1114/faulty/m2_bakery_bill_old.prog.abap | 181 ++++++++++ .../G1114/faulty/m3_bakery_bill_old.prog.abap | 181 ++++++++++ .../G1114/faulty/m4_bakery_bill_old.prog.abap | 181 ++++++++++ tasks_gen/train/G1114/generation.json | 28 ++ .../G1114/hidden/bakery_hidden.clas.abap | 117 +++++++ tasks_gen/train/G1114/mutation.json | 73 ++++ .../G1114/reference/bakery_bill_old.prog.abap | 181 ++++++++++ .../G1114/seed/bakery_bill_old.prog.abap | 74 ++++ .../train/G1114/seed/bakery_seed.clas.abap | 27 ++ tasks_gen/train/G1114/seed/deliv.tabl.asabap | 13 + tasks_gen/train/G1114/seed/shop.tabl.asabap | 14 + tasks_gen/train/G1114/spec.md | 43 +++ tasks_gen/train/G1114/task.json | 75 ++++ .../G1115/faulty/m0_trans_pricer.clas.abap | 92 +++++ .../train/G1115/faulty/m1_trans_fee.func.abap | 28 ++ .../G1115/faulty/m2_trans_pricer.clas.abap | 92 +++++ .../train/G1115/faulty/m3_trans_fee.func.abap | 28 ++ .../G1115/faulty/m4_trans_pricer.clas.abap | 92 +++++ tasks_gen/train/G1115/generation.json | 28 ++ .../train/G1115/hidden/t14_hidden.clas.abap | 140 ++++++++ tasks_gen/train/G1115/mutation.json | 90 +++++ .../train/G1115/reference/t14_test.clas.abap | 73 ++++ .../train/G1115/reference/trans_fee.func.abap | 28 ++ .../G1115/reference/trans_pricer.clas.abap | 92 +++++ .../reference/trans_pricer.testclasses.abap | 40 +++ .../train/G1115/seed/trans_sample.func.abap | 15 + tasks_gen/train/G1115/spec.md | 86 +++++ tasks_gen/train/G1115/task.json | 95 +++++ .../G1116/faulty/m0_carpet_fee.func.abap | 55 +++ .../G1116/faulty/m1_carpet_fee.func.abap | 55 +++ .../G1116/faulty/m2_carpet_fee.func.abap | 55 +++ .../G1116/faulty/m3_carpet_fee.func.abap | 55 +++ .../G1116/faulty/m4_carpet_fee.func.abap | 55 +++ tasks_gen/train/G1116/generation.json | 19 + .../train/G1116/hidden/t42_hidden.clas.abap | 134 ++++++++ tasks_gen/train/G1116/mutation.json | 68 ++++ .../G1116/reference/carpet_fee.func.abap | 55 +++ .../train/G1116/reference/t42_test.clas.abap | 114 ++++++ .../train/G1116/seed/carpet_sample.func.abap | 20 ++ tasks_gen/train/G1116/spec.md | 62 ++++ tasks_gen/train/G1116/task.json | 84 +++++ tasks_gen/train/G1117/generation.json | 44 +++ .../train/G1117/hidden/t21_hidden.clas.abap | 117 +++++++ tasks_gen/train/G1117/mutation.json | 75 ++++ .../G1117/reference/parcel_fee.func.abap | 71 ++++ .../train/G1117/reference/t21_test.clas.abap | 80 +++++ .../train/G1117/seed/zone_lookup.func.abap | 31 ++ tasks_gen/train/G1117/spec.md | 58 ++++ tasks_gen/train/G1117/task.json | 88 +++++ .../train/G1118/faulty/m0_subs_sum.prog.abap | 111 ++++++ .../train/G1118/faulty/m1_subs_sum.prog.abap | 111 ++++++ .../train/G1118/faulty/m2_subs_sum.prog.abap | 111 ++++++ .../train/G1118/faulty/m3_subs_sum.prog.abap | 111 ++++++ .../train/G1118/faulty/m4_subs_sum.prog.abap | 111 ++++++ tasks_gen/train/G1118/generation.json | 24 ++ .../train/G1118/hidden/t15_hidden.clas.abap | 100 ++++++ tasks_gen/train/G1118/mutation.json | 82 +++++ .../train/G1118/reference/subs_sum.prog.abap | 111 ++++++ tasks_gen/train/G1118/seed/subs.tabl.asabap | 13 + tasks_gen/train/G1118/seed/t15_seed.clas.abap | 20 ++ tasks_gen/train/G1118/spec.md | 38 ++ tasks_gen/train/G1118/task.json | 60 ++++ tasks_gen/train/G1119/generation.json | 14 + .../train/G1119/reference/bind_fee.func.abap | 51 +++ .../train/G1119/reference/bind_test.clas.abap | 89 +++++ .../train/G1119/seed/bind_sample.func.abap | 19 + tasks_gen/train/G1119/spec.md | 45 +++ tasks_gen/train/G1119/task.json | 100 ++++++ .../G1120/faulty/m0_i_brick_size.ddls.asddls | 18 + .../G1120/faulty/m1_i_brick_size.ddls.asddls | 18 + .../G1120/faulty/m2_i_brick_size.ddls.asddls | 18 + .../G1120/faulty/m3_i_brick_size.ddls.asddls | 18 + .../G1120/faulty/m4_i_brick_size.ddls.asddls | 18 + tasks_gen/train/G1120/generation.json | 19 + .../train/G1120/hidden/t31_hidden.clas.abap | 86 +++++ tasks_gen/train/G1120/mutation.json | 58 ++++ .../G1120/reference/i_brick_size.ddls.asddls | 18 + .../train/G1120/reference/t31_test.clas.abap | 39 +++ tasks_gen/train/G1120/seed/brick.tabl.asabap | 14 + tasks_gen/train/G1120/spec.md | 51 +++ tasks_gen/train/G1120/task.json | 60 ++++ .../G1121/faulty/m0_mush_harvest.func.abap | 62 ++++ .../G1121/faulty/m1_mush_harvest.func.abap | 62 ++++ .../G1121/faulty/m2_mush_harvest.func.abap | 62 ++++ .../G1121/faulty/m3_mush_harvest.func.abap | 62 ++++ .../G1121/faulty/m4_mush_harvest.func.abap | 62 ++++ tasks_gen/train/G1121/generation.json | 34 ++ .../G1121/hidden/t_mush_hidden.clas.abap | 171 +++++++++ tasks_gen/train/G1121/mutation.json | 76 ++++ .../G1121/reference/mush_harvest.func.abap | 62 ++++ .../G1121/reference/t_mush_test.clas.abap | 89 +++++ tasks_gen/train/G1121/seed/harvest.tabl.abap | 10 + tasks_gen/train/G1121/spec.md | 54 +++ tasks_gen/train/G1121/task.json | 84 +++++ tasks_gen/train/G1122/generation.json | 57 +++ .../train/G1122/hidden/ext_hidden.clas.abap | 148 ++++++++ tasks_gen/train/G1122/mutation.json | 39 +++ .../G1122/reference/ext_check_old.prog.abap | 209 +++++++++++ .../train/G1122/seed/ext_check_old.prog.abap | 80 +++++ tasks_gen/train/G1122/seed/ext_seed.clas.abap | 19 + tasks_gen/train/G1122/seed/exting.tabl.asabap | 15 + tasks_gen/train/G1122/spec.md | 47 +++ tasks_gen/train/G1122/task.json | 70 ++++ tasks_gen/train/G1123/generation.json | 38 ++ .../train/G1123/hidden/t02_hidden.clas.abap | 142 ++++++++ .../G1123/reference/airport_fee.clas.abap | 124 +++++++ .../reference/airport_fee.testclasses.abap | 95 +++++ tasks_gen/train/G1123/spec.md | 78 +++++ tasks_gen/train/G1123/task.json | 41 +++ .../G1124/faulty/m0_shift_booker.clas.abap | 108 ++++++ .../G1124/faulty/m1_shift_booker.clas.abap | 108 ++++++ .../G1124/faulty/m2_shift_booker.clas.abap | 108 ++++++ .../G1124/faulty/m3_shift_booker.clas.abap | 108 ++++++ .../G1124/faulty/m4_shift_booker.clas.abap | 108 ++++++ tasks_gen/train/G1124/generation.json | 24 ++ .../train/G1124/hidden/d02_hidden.clas.abap | 139 ++++++++ tasks_gen/train/G1124/mutation.json | 70 ++++ .../G1124/reference/cx_shift_error.clas.abap | 9 + .../G1124/reference/shift_booker.clas.abap | 108 ++++++ .../reference/shift_booker.testclasses.abap | 105 ++++++ tasks_gen/train/G1124/spec.md | 76 ++++ tasks_gen/train/G1124/task.json | 51 +++ .../train/G1125/faulty/m0_prt_price.func.abap | 56 +++ .../train/G1125/faulty/m2_prt_price.func.abap | 56 +++ .../train/G1125/faulty/m3_prt_price.func.abap | 56 +++ .../train/G1125/faulty/m4_prt_price.func.abap | 56 +++ tasks_gen/train/G1125/generation.json | 24 ++ .../train/G1125/hidden/prt_hidden.clas.abap | 121 +++++++ tasks_gen/train/G1125/mutation.json | 59 ++++ .../train/G1125/reference/prt_price.func.abap | 56 +++ .../train/G1125/reference/prt_test.clas.abap | 107 ++++++ .../train/G1125/seed/prt_sample.func.abap | 18 + tasks_gen/train/G1125/spec.md | 45 +++ tasks_gen/train/G1125/task.json | 88 +++++ tasks_gen/train/G1126/generation.json | 37 ++ .../train/G1126/hidden/vet_hidden.clas.abap | 135 ++++++++ tasks_gen/train/G1126/mutation.json | 42 +++ .../train/G1126/reference/vet_sum.prog.abap | 203 +++++++++++ tasks_gen/train/G1126/seed/treat.tabl.asabap | 12 + tasks_gen/train/G1126/seed/vet_seed.clas.abap | 66 ++++ tasks_gen/train/G1126/spec.md | 45 +++ tasks_gen/train/G1126/task.json | 60 ++++ tasks_gen/train/G1127/generation.json | 14 + .../train/G1127/reference/key_price.func.abap | 34 ++ .../train/G1127/reference/key_test.clas.abap | 49 +++ .../train/G1127/seed/key_sample.func.abap | 14 + tasks_gen/train/G1127/spec.md | 40 +++ tasks_gen/train/G1127/task.json | 92 +++++ tasks_gen/train/G1128/generation.json | 37 ++ .../train/G1128/hidden/t16_hidden.clas.abap | 100 ++++++ tasks_gen/train/G1128/mutation.json | 47 +++ .../train/G1128/reference/i_route.ddls.asddls | 14 + .../train/G1128/reference/i_stop.ddls.asddls | 10 + .../train/G1128/reference/t16_test.clas.abap | 51 +++ tasks_gen/train/G1128/seed/depot.tabl.asabap | 11 + tasks_gen/train/G1128/seed/route.tabl.asabap | 12 + tasks_gen/train/G1128/seed/stop.tabl.asabap | 12 + tasks_gen/train/G1128/spec.md | 51 +++ tasks_gen/train/G1128/task.json | 90 +++++ tasks_gen/train/G1129/generation.json | 38 ++ .../train/G1129/hidden/t14_hidden.clas.abap | 325 ++++++++++++++++++ .../train/G1129/reference/meal_bill.func.abap | 56 +++ .../train/G1129/reference/t14_test.clas.abap | 135 ++++++++ .../train/G1129/seed/meal_data.clas.abap | 23 ++ tasks_gen/train/G1129/seed/mealbk.tabl.abap | 14 + tasks_gen/train/G1129/spec.md | 60 ++++ tasks_gen/train/G1129/task.json | 91 +++++ .../G1130/faulty/m0_groom_bill_old.prog.abap | 137 ++++++++ .../G1130/faulty/m1_groom_bill_old.prog.abap | 137 ++++++++ .../G1130/faulty/m2_groom_bill_old.prog.abap | 137 ++++++++ .../G1130/faulty/m3_groom_bill_old.prog.abap | 137 ++++++++ .../G1130/faulty/m4_groom_bill_old.prog.abap | 137 ++++++++ tasks_gen/train/G1130/generation.json | 19 + .../train/G1130/hidden/groom_hidden.clas.abap | 94 +++++ tasks_gen/train/G1130/mutation.json | 72 ++++ .../G1130/reference/groom_bill_old.prog.abap | 137 ++++++++ tasks_gen/train/G1130/seed/groom.tabl.asabap | 14 + .../train/G1130/seed/groom_bill_old.prog.abap | 59 ++++ .../train/G1130/seed/groom_seed.clas.abap | 19 + tasks_gen/train/G1130/spec.md | 41 +++ tasks_gen/train/G1130/task.json | 68 ++++ .../G1131/faulty/m0_i_honey_yield.ddls.asddls | 16 + .../G1131/faulty/m1_i_honey_yield.ddls.asddls | 16 + .../G1131/faulty/m2_i_honey_yield.ddls.asddls | 16 + tasks_gen/train/G1131/generation.json | 19 + .../train/G1131/hidden/t41_hidden.clas.abap | 90 +++++ tasks_gen/train/G1131/mutation.json | 42 +++ .../G1131/reference/i_honey_yield.ddls.asddls | 16 + .../train/G1131/reference/t41_test.clas.abap | 48 +++ tasks_gen/train/G1131/seed/apiary.tabl.asabap | 12 + tasks_gen/train/G1131/seed/honey.tabl.asabap | 13 + .../G1131/seed/i_honey_yield.ddls.asddls | 16 + tasks_gen/train/G1131/spec.md | 50 +++ tasks_gen/train/G1131/task.json | 72 ++++ .../G1132/faulty/m0_walker_stats.prog.abap | 106 ++++++ .../G1132/faulty/m1_walker_stats.prog.abap | 106 ++++++ .../G1132/faulty/m2_walker_stats.prog.abap | 106 ++++++ .../G1132/faulty/m3_walker_stats.prog.abap | 106 ++++++ .../G1132/faulty/m4_walker_stats.prog.abap | 106 ++++++ tasks_gen/train/G1132/generation.json | 33 ++ .../train/G1132/hidden/walk_check.clas.abap | 102 ++++++ tasks_gen/train/G1132/mutation.json | 75 ++++ .../G1132/reference/walker_stats.prog.abap | 106 ++++++ tasks_gen/train/G1132/seed/walk.tabl.asabap | 13 + .../train/G1132/seed/walk_fill.clas.abap | 21 ++ .../train/G1132/seed/walksum.tabl.asabap | 10 + tasks_gen/train/G1132/spec.md | 45 +++ tasks_gen/train/G1132/task.json | 67 ++++ .../train/G1133/faulty/m1_taxi_day.prog.abap | 245 +++++++++++++ .../train/G1133/faulty/m2_taxi_day.prog.abap | 245 +++++++++++++ .../train/G1133/faulty/m3_taxi_day.prog.abap | 245 +++++++++++++ .../train/G1133/faulty/m4_taxi_day.prog.abap | 245 +++++++++++++ tasks_gen/train/G1133/generation.json | 34 ++ .../train/G1133/hidden/taxi_hidden.clas.abap | 134 ++++++++ tasks_gen/train/G1133/mutation.json | 60 ++++ .../train/G1133/reference/taxi_day.prog.abap | 245 +++++++++++++ tasks_gen/train/G1133/seed/driver.tabl.asabap | 11 + .../train/G1133/seed/taxi_seed.clas.abap | 31 ++ tasks_gen/train/G1133/seed/trip.tabl.asabap | 13 + tasks_gen/train/G1133/spec.md | 50 +++ tasks_gen/train/G1133/task.json | 69 ++++ .../G1135/faulty/m0_pool_ticket.clas.abap | 145 ++++++++ .../G1135/faulty/m2_pool_ticket.clas.abap | 145 ++++++++ .../G1135/faulty/m3_pool_ticket.clas.abap | 145 ++++++++ tasks_gen/train/G1135/generation.json | 19 + .../train/G1135/hidden/t31_hidden.clas.abap | 113 ++++++ tasks_gen/train/G1135/mutation.json | 61 ++++ .../G1135/reference/pool_ticket.clas.abap | 145 ++++++++ .../reference/pool_ticket.testclasses.abap | 75 ++++ tasks_gen/train/G1135/spec.md | 70 ++++ tasks_gen/train/G1135/task.json | 42 +++ tasks_gen/train/G1136/generation.json | 29 ++ .../G1136/hidden/t_d_job_hidden.clas.abap | 202 +++++++++++ .../G1136/reference/cx_job_error.clas.abap | 60 ++++ .../G1136/reference/job_planner.clas.abap | 132 +++++++ .../reference/job_planner.testclasses.abap | 95 +++++ tasks_gen/train/G1136/spec.md | 87 +++++ tasks_gen/train/G1136/task.json | 57 +++ .../G1137/faulty/m0_tattoo_fee.func.abap | 59 ++++ .../G1137/faulty/m1_tattoo_fee.func.abap | 59 ++++ .../G1137/faulty/m2_tattoo_fee.func.abap | 59 ++++ .../G1137/faulty/m3_tattoo_fee.func.abap | 59 ++++ .../G1137/faulty/m4_tattoo_fee.func.abap | 59 ++++ tasks_gen/train/G1137/generation.json | 28 ++ .../train/G1137/hidden/t42_hidden.clas.abap | 102 ++++++ tasks_gen/train/G1137/mutation.json | 78 +++++ .../train/G1137/reference/t42_test.clas.abap | 79 +++++ .../G1137/reference/tattoo_fee.func.abap | 59 ++++ .../train/G1137/seed/tattoo_sample.func.abap | 14 + tasks_gen/train/G1137/spec.md | 47 +++ tasks_gen/train/G1137/task.json | 80 +++++ .../G1138/faulty/m0_line_output.prog.abap | 139 ++++++++ .../G1138/faulty/m1_line_output.prog.abap | 139 ++++++++ .../G1138/faulty/m2_line_output.prog.abap | 139 ++++++++ .../G1138/faulty/m3_line_output.prog.abap | 139 ++++++++ .../G1138/faulty/m4_line_output.prog.abap | 139 ++++++++ tasks_gen/train/G1138/generation.json | 24 ++ .../train/G1138/hidden/line_out_hid.clas.abap | 94 +++++ tasks_gen/train/G1138/mutation.json | 64 ++++ .../G1138/reference/line_output.prog.abap | 139 ++++++++ tasks_gen/train/G1138/seed/bottle.tabl.asabap | 14 + .../train/G1138/seed/seed_bottle.clas.abap | 20 ++ tasks_gen/train/G1138/spec.md | 47 +++ tasks_gen/train/G1138/task.json | 60 ++++ tasks_gen/train/G1139/generation.json | 23 ++ .../G1139/reference/loan_check.func.abap | 36 ++ .../train/G1139/reference/loan_test.clas.abap | 91 +++++ .../train/G1139/seed/loan_sample.func.abap | 14 + tasks_gen/train/G1139/spec.md | 40 +++ tasks_gen/train/G1139/task.json | 94 +++++ .../train/G1140/hidden/mill_hidden.clas.abap | 104 ++++++ tasks_gen/train/G1140/mutation.json | 44 +++ .../G1140/reference/oil_report.prog.abap | 105 ++++++ tasks_gen/train/G1140/seed/grove.tabl.asabap | 12 + .../train/G1140/seed/mill_seed.clas.abap | 24 ++ tasks_gen/train/G1140/seed/press.tabl.asabap | 13 + tasks_gen/train/G1140/spec.md | 41 +++ tasks_gen/train/G1140/task.json | 67 ++++ .../train/G1141/faulty/m1_exhib_cmp.prog.abap | 134 ++++++++ .../train/G1141/faulty/m2_exhib_cmp.prog.abap | 134 ++++++++ .../train/G1141/faulty/m3_exhib_cmp.prog.abap | 134 ++++++++ tasks_gen/train/G1141/generation.json | 24 ++ .../train/G1141/hidden/t31_hidden.clas.abap | 139 ++++++++ tasks_gen/train/G1141/mutation.json | 50 +++ .../train/G1141/reference/exhib_cmp.prog.abap | 134 ++++++++ tasks_gen/train/G1141/seed/exhib.tabl.asabap | 11 + tasks_gen/train/G1141/seed/t31_seed.clas.abap | 26 ++ tasks_gen/train/G1141/seed/visit.tabl.asabap | 12 + tasks_gen/train/G1141/spec.md | 51 +++ tasks_gen/train/G1141/task.json | 69 ++++ tasks_gen/train/G1142/generation.json | 38 ++ .../G1142/hidden/hut_fee_hidden.clas.abap | 141 ++++++++ tasks_gen/train/G1142/mutation.json | 76 ++++ .../train/G1142/reference/hut_fee.func.abap | 59 ++++ .../G1142/reference/hut_fee_test.clas.abap | 89 +++++ tasks_gen/train/G1142/seed/hut_fee.func.abap | 67 ++++ tasks_gen/train/G1142/spec.md | 48 +++ tasks_gen/train/G1142/task.json | 85 +++++ .../train/G1144/faulty/m0_move_fee.func.abap | 66 ++++ .../train/G1144/faulty/m1_move_fee.func.abap | 66 ++++ .../train/G1144/faulty/m2_move_fee.func.abap | 66 ++++ .../train/G1144/faulty/m3_move_fee.func.abap | 66 ++++ .../train/G1144/faulty/m4_move_fee.func.abap | 66 ++++ tasks_gen/train/G1144/generation.json | 24 ++ .../train/G1144/hidden/move_hidden.clas.abap | 135 ++++++++ tasks_gen/train/G1144/mutation.json | 70 ++++ .../train/G1144/reference/move_fee.func.abap | 66 ++++ .../train/G1144/reference/move_test.clas.abap | 104 ++++++ .../train/G1144/seed/move_sample.func.abap | 18 + tasks_gen/train/G1144/spec.md | 58 ++++ tasks_gen/train/G1144/task.json | 92 +++++ .../faulty/m0_i_still_output.ddls.asddls | 14 + .../faulty/m1_i_still_output.ddls.asddls | 14 + .../faulty/m2_i_still_output.ddls.asddls | 14 + tasks_gen/train/G1145/generation.json | 28 ++ .../G1145/hidden/t_runlog_hidden.clas.abap | 78 +++++ tasks_gen/train/G1145/mutation.json | 40 +++ .../reference/i_still_output.ddls.asddls | 14 + .../G1145/reference/runlog_out_test.clas.abap | 43 +++ tasks_gen/train/G1145/seed/runlog.tabl.asabap | 15 + .../train/G1145/seed/seed_runlog.clas.abap | 30 ++ tasks_gen/train/G1145/spec.md | 42 +++ tasks_gen/train/G1145/task.json | 67 ++++ tasks_gen/train/G1147/generation.json | 14 + .../G1147/reference/charge_stat.prog.abap | 85 +++++ tasks_gen/train/G1147/seed/charge.tabl.asabap | 12 + .../train/G1147/seed/charge_seed.clas.abap | 17 + tasks_gen/train/G1147/spec.md | 40 +++ tasks_gen/train/G1147/task.json | 73 ++++ .../G1148/faulty/m0_studio_bill.prog.abap | 122 +++++++ .../G1148/faulty/m1_studio_bill.prog.abap | 122 +++++++ .../G1148/faulty/m2_studio_bill.prog.abap | 122 +++++++ .../G1148/faulty/m3_studio_bill.prog.abap | 122 +++++++ .../G1148/faulty/m4_studio_bill.prog.abap | 122 +++++++ tasks_gen/train/G1148/generation.json | 44 +++ .../train/G1148/hidden/t15_hidden.clas.abap | 132 +++++++ tasks_gen/train/G1148/mutation.json | 73 ++++ .../G1148/reference/studio_bill.prog.abap | 122 +++++++ tasks_gen/train/G1148/seed/studio.tabl.asabap | 13 + tasks_gen/train/G1148/seed/t15_seed.clas.abap | 18 + tasks_gen/train/G1148/spec.md | 43 +++ tasks_gen/train/G1148/task.json | 61 ++++ .../G1150/faulty/m0_farrier_fee.func.abap | 71 ++++ .../G1150/faulty/m1_farrier_fee.func.abap | 71 ++++ .../G1150/faulty/m2_farrier_fee.func.abap | 71 ++++ .../G1150/faulty/m3_farrier_fee.func.abap | 71 ++++ .../G1150/faulty/m4_farrier_fee.func.abap | 71 ++++ tasks_gen/train/G1150/generation.json | 65 ++++ .../G1150/hidden/farrier_hidden.clas.abap | 169 +++++++++ tasks_gen/train/G1150/mutation.json | 76 ++++ .../G1150/reference/farrier_fee.func.abap | 71 ++++ .../G1150/reference/farrier_test.clas.abap | 108 ++++++ .../train/G1150/seed/farrier_fee.func.abap | 64 ++++ tasks_gen/train/G1150/spec.md | 64 ++++ tasks_gen/train/G1150/task.json | 90 +++++ tasks_gen/train/G1153/generation.json | 33 ++ .../G1153/hidden/dealer_hidden.clas.abap | 102 ++++++ tasks_gen/train/G1153/mutation.json | 40 +++ .../G1153/reference/dealer_sum.prog.abap | 167 +++++++++ tasks_gen/train/G1153/seed/dealer.tabl.asabap | 11 + .../train/G1153/seed/dealer_seed.clas.abap | 29 ++ tasks_gen/train/G1153/seed/vsale.tabl.asabap | 13 + tasks_gen/train/G1153/spec.md | 50 +++ tasks_gen/train/G1153/task.json | 68 ++++ tasks_gen/train/_logs/G1042.json | 26 ++ tasks_gen/train/_logs/G1044.json | 26 ++ tasks_gen/train/_logs/G1045.json | 21 ++ tasks_gen/train/_logs/G1047.json | 36 ++ tasks_gen/train/_logs/G1048.json | 31 ++ tasks_gen/train/_logs/G1050.json | 46 +++ tasks_gen/train/_logs/G1051.json | 31 ++ tasks_gen/train/_logs/G1053.json | 31 ++ tasks_gen/train/_logs/G1054.json | 26 ++ tasks_gen/train/_logs/G1056.json | 31 ++ tasks_gen/train/_logs/G1057.json | 36 ++ tasks_gen/train/_logs/G1058.json | 21 ++ tasks_gen/train/_logs/G1059.json | 16 + tasks_gen/train/_logs/G1060.json | 40 +++ tasks_gen/train/_logs/G1061.json | 26 ++ tasks_gen/train/_logs/G1062.json | 21 ++ tasks_gen/train/_logs/G1063.json | 21 ++ tasks_gen/train/_logs/G1064.json | 36 ++ tasks_gen/train/_logs/G1065.json | 21 ++ tasks_gen/train/_logs/G1066.json | 21 ++ tasks_gen/train/_logs/G1067.json | 33 ++ tasks_gen/train/_logs/G1068.json | 46 +++ tasks_gen/train/_logs/G1069.json | 21 ++ tasks_gen/train/_logs/G1070.json | 21 ++ tasks_gen/train/_logs/G1071.json | 42 +++ tasks_gen/train/_logs/G1072.json | 21 ++ tasks_gen/train/_logs/G1073.json | 36 ++ tasks_gen/train/_logs/G1074.json | 33 ++ tasks_gen/train/_logs/G1075.json | 26 ++ tasks_gen/train/_logs/G1076.json | 36 ++ tasks_gen/train/_logs/G1077.json | 21 ++ tasks_gen/train/_logs/G1078.json | 32 ++ tasks_gen/train/_logs/G1079.json | 25 ++ tasks_gen/train/_logs/G1080.json | 26 ++ tasks_gen/train/_logs/G1081.json | 26 ++ tasks_gen/train/_logs/G1082.json | 39 +++ tasks_gen/train/_logs/G1083.json | 26 ++ tasks_gen/train/_logs/G1084.json | 26 ++ tasks_gen/train/_logs/G1085.json | 21 ++ tasks_gen/train/_logs/G1086.json | 21 ++ tasks_gen/train/_logs/G1087.json | 16 + tasks_gen/train/_logs/G1088.json | 62 ++++ tasks_gen/train/_logs/G1089.json | 21 ++ tasks_gen/train/_logs/G1090.json | 21 ++ tasks_gen/train/_logs/G1091.json | 21 ++ tasks_gen/train/_logs/G1092.json | 40 +++ tasks_gen/train/_logs/G1093.json | 44 +++ tasks_gen/train/_logs/G1094.json | 52 +++ tasks_gen/train/_logs/G1095.json | 26 ++ tasks_gen/train/_logs/G1096.json | 30 ++ tasks_gen/train/_logs/G1097.json | 21 ++ tasks_gen/train/_logs/G1098.json | 21 ++ tasks_gen/train/_logs/G1099.json | 16 + tasks_gen/train/_logs/G1100.json | 39 +++ tasks_gen/train/_logs/G1101.json | 38 ++ tasks_gen/train/_logs/G1102.json | 48 +++ tasks_gen/train/_logs/G1103.json | 26 ++ tasks_gen/train/_logs/G1104.json | 36 ++ tasks_gen/train/_logs/G1105.json | 21 ++ tasks_gen/train/_logs/G1106.json | 39 +++ tasks_gen/train/_logs/G1107.json | 16 + tasks_gen/train/_logs/G1108.json | 52 +++ tasks_gen/train/_logs/G1109.json | 42 +++ tasks_gen/train/_logs/G1110.json | 21 ++ tasks_gen/train/_logs/G1111.json | 21 ++ tasks_gen/train/_logs/G1112.json | 21 ++ tasks_gen/train/_logs/G1113.json | 31 ++ tasks_gen/train/_logs/G1114.json | 30 ++ tasks_gen/train/_logs/G1115.json | 30 ++ tasks_gen/train/_logs/G1116.json | 21 ++ tasks_gen/train/_logs/G1117.json | 46 +++ tasks_gen/train/_logs/G1118.json | 26 ++ tasks_gen/train/_logs/G1119.json | 16 + tasks_gen/train/_logs/G1120.json | 21 ++ tasks_gen/train/_logs/G1121.json | 36 ++ tasks_gen/train/_logs/G1122.json | 59 ++++ tasks_gen/train/_logs/G1123.json | 40 +++ tasks_gen/train/_logs/G1124.json | 26 ++ tasks_gen/train/_logs/G1125.json | 26 ++ tasks_gen/train/_logs/G1126.json | 39 +++ tasks_gen/train/_logs/G1127.json | 16 + tasks_gen/train/_logs/G1128.json | 39 +++ tasks_gen/train/_logs/G1129.json | 40 +++ tasks_gen/train/_logs/G1130.json | 21 ++ tasks_gen/train/_logs/G1131.json | 21 ++ tasks_gen/train/_logs/G1132.json | 35 ++ tasks_gen/train/_logs/G1133.json | 36 ++ tasks_gen/train/_logs/G1134.json | 52 +++ tasks_gen/train/_logs/G1135.json | 21 ++ tasks_gen/train/_logs/G1136.json | 31 ++ tasks_gen/train/_logs/G1137.json | 30 ++ tasks_gen/train/_logs/G1138.json | 26 ++ tasks_gen/train/_logs/G1139.json | 25 ++ tasks_gen/train/_logs/G1141.json | 26 ++ tasks_gen/train/_logs/G1142.json | 40 +++ tasks_gen/train/_logs/G1144.json | 26 ++ tasks_gen/train/_logs/G1145.json | 30 ++ tasks_gen/train/_logs/G1147.json | 16 + tasks_gen/train/_logs/G1148.json | 46 +++ tasks_gen/train/_logs/G1150.json | 67 ++++ tasks_gen/train/_logs/G1153.json | 35 ++ train/STATE.md | 11 + 1230 files changed, 77563 insertions(+), 33 deletions(-) create mode 100644 tasks_gen/train/G1042/faulty/m0_snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/faulty/m1_snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/faulty/m2_snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/faulty/m3_snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/faulty/m4_snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/generation.json create mode 100644 tasks_gen/train/G1042/mutation.json create mode 100644 tasks_gen/train/G1044/faulty/m2_escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/faulty/m3_escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/faulty/m4_escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/generation.json create mode 100644 tasks_gen/train/G1044/mutation.json create mode 100644 tasks_gen/train/G1045/faulty/m0_cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/faulty/m1_cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/faulty/m2_cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/faulty/m3_cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/faulty/m4_cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/generation.json create mode 100644 tasks_gen/train/G1045/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1045/mutation.json create mode 100644 tasks_gen/train/G1045/reference/cinema_pricer.clas.abap create mode 100644 tasks_gen/train/G1045/reference/cinema_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1045/seed/if_cinema_price.intf.abap create mode 100644 tasks_gen/train/G1045/spec.md create mode 100644 tasks_gen/train/G1045/task.json create mode 100644 tasks_gen/train/G1047/generation.json create mode 100644 tasks_gen/train/G1047/reference/gravel_pricer.clas.abap create mode 100644 tasks_gen/train/G1047/reference/gravel_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1047/seed/grv_ord.tabl.abap create mode 100644 tasks_gen/train/G1047/seed/seed_grv.clas.abap create mode 100644 tasks_gen/train/G1047/spec.md create mode 100644 tasks_gen/train/G1047/task.json create mode 100644 tasks_gen/train/G1048/faulty/m0_i_truck.ddls.asddls create mode 100644 tasks_gen/train/G1048/faulty/m1_i_truck.ddls.asddls create mode 100644 tasks_gen/train/G1048/faulty/m2_i_truck.ddls.asddls create mode 100644 tasks_gen/train/G1048/faulty/m3_i_truck.ddls.asddls create mode 100644 tasks_gen/train/G1048/generation.json create mode 100644 tasks_gen/train/G1048/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1048/mutation.json create mode 100644 tasks_gen/train/G1048/reference/i_truck.ddls.asddls create mode 100644 tasks_gen/train/G1048/reference/t21_test.clas.abap create mode 100644 tasks_gen/train/G1048/seed/depot.tabl.asabap create mode 100644 tasks_gen/train/G1048/seed/seed_data.clas.abap create mode 100644 tasks_gen/train/G1048/seed/truck.tabl.asabap create mode 100644 tasks_gen/train/G1048/spec.md create mode 100644 tasks_gen/train/G1048/task.json create mode 100644 tasks_gen/train/G1050/faulty/m0_tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/faulty/m1_tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/faulty/m2_tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/faulty/m3_tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/faulty/m4_tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/generation.json create mode 100644 tasks_gen/train/G1050/hidden/tailor_pricer_hidden.clas.abap create mode 100644 tasks_gen/train/G1050/mutation.json create mode 100644 tasks_gen/train/G1050/reference/tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/reference/tailor_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1050/seed/tailor_pricer.clas.abap create mode 100644 tasks_gen/train/G1050/spec.md create mode 100644 tasks_gen/train/G1050/task.json create mode 100644 tasks_gen/train/G1051/faulty/m1_i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/faulty/m2_i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/faulty/m3_i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/faulty/m4_i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/generation.json create mode 100644 tasks_gen/train/G1051/hidden/melt_hidden.clas.abap create mode 100644 tasks_gen/train/G1051/mutation.json create mode 100644 tasks_gen/train/G1051/reference/i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/reference/melt_test.clas.abap create mode 100644 tasks_gen/train/G1051/seed/casting.tabl.asabap create mode 100644 tasks_gen/train/G1051/seed/i_melt_out.ddls.asddls create mode 100644 tasks_gen/train/G1051/seed/melt.tabl.asabap create mode 100644 tasks_gen/train/G1051/spec.md create mode 100644 tasks_gen/train/G1051/task.json create mode 100644 tasks_gen/train/G1053/faulty/m0_rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/faulty/m1_rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/faulty/m2_rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/faulty/m3_rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/faulty/m4_rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/generation.json create mode 100644 tasks_gen/train/G1053/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1053/mutation.json create mode 100644 tasks_gen/train/G1053/reference/rack_billing.clas.abap create mode 100644 tasks_gen/train/G1053/reference/rack_billing.testclasses.abap create mode 100644 tasks_gen/train/G1053/spec.md create mode 100644 tasks_gen/train/G1053/task.json create mode 100644 tasks_gen/train/G1054/faulty/m0_pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/faulty/m1_pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/faulty/m2_pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/faulty/m3_pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/faulty/m4_pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/generation.json create mode 100644 tasks_gen/train/G1054/hidden/pest_test_hidden.clas.abap create mode 100644 tasks_gen/train/G1054/mutation.json create mode 100644 tasks_gen/train/G1054/reference/pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/reference/pest_fee.testclasses.abap create mode 100644 tasks_gen/train/G1054/seed/pest_fee.clas.abap create mode 100644 tasks_gen/train/G1054/spec.md create mode 100644 tasks_gen/train/G1054/task.json create mode 100644 tasks_gen/train/G1056/faulty/m0_tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/faulty/m1_tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/faulty/m2_tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/faulty/m3_tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/faulty/m4_tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/generation.json create mode 100644 tasks_gen/train/G1056/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1056/mutation.json create mode 100644 tasks_gen/train/G1056/reference/cx_tyre_error.clas.abap create mode 100644 tasks_gen/train/G1056/reference/cx_tyre_load.clas.abap create mode 100644 tasks_gen/train/G1056/reference/cx_tyre_qty.clas.abap create mode 100644 tasks_gen/train/G1056/reference/cx_tyre_size.clas.abap create mode 100644 tasks_gen/train/G1056/reference/cx_tyre_speed.clas.abap create mode 100644 tasks_gen/train/G1056/reference/tyre_checker.clas.abap create mode 100644 tasks_gen/train/G1056/reference/tyre_checker.testclasses.abap create mode 100644 tasks_gen/train/G1056/seed/if_tyre_order.intf.abap create mode 100644 tasks_gen/train/G1056/spec.md create mode 100644 tasks_gen/train/G1056/task.json create mode 100644 tasks_gen/train/G1057/faulty/m0_air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/faulty/m1_air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/faulty/m2_air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/faulty/m3_air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/faulty/m4_air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/generation.json create mode 100644 tasks_gen/train/G1057/hidden/air_frt_hidden.clas.abap create mode 100644 tasks_gen/train/G1057/mutation.json create mode 100644 tasks_gen/train/G1057/reference/air_freight_pricer.clas.abap create mode 100644 tasks_gen/train/G1057/reference/air_freight_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1057/seed/if_air_freight.intf.abap create mode 100644 tasks_gen/train/G1057/spec.md create mode 100644 tasks_gen/train/G1057/task.json create mode 100644 tasks_gen/train/G1058/faulty/m0_ice_fee.func.abap create mode 100644 tasks_gen/train/G1058/faulty/m2_ice_fee.func.abap create mode 100644 tasks_gen/train/G1058/faulty/m3_ice_fee.func.abap create mode 100644 tasks_gen/train/G1058/faulty/m4_ice_fee.func.abap create mode 100644 tasks_gen/train/G1058/generation.json create mode 100644 tasks_gen/train/G1058/hidden/t37_hidden.clas.abap create mode 100644 tasks_gen/train/G1058/mutation.json create mode 100644 tasks_gen/train/G1058/reference/ice_fee.func.abap create mode 100644 tasks_gen/train/G1058/reference/t37_test.clas.abap create mode 100644 tasks_gen/train/G1058/seed/ice_sample.func.abap create mode 100644 tasks_gen/train/G1058/spec.md create mode 100644 tasks_gen/train/G1058/task.json create mode 100644 tasks_gen/train/G1059/generation.json create mode 100644 tasks_gen/train/G1059/reference/sauna_pricer.clas.abap create mode 100644 tasks_gen/train/G1059/reference/sauna_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1059/seed/if_sauna_price.intf.abap create mode 100644 tasks_gen/train/G1059/spec.md create mode 100644 tasks_gen/train/G1059/task.json create mode 100644 tasks_gen/train/G1060/faulty/m0_i_air_quality.ddls.asddls create mode 100644 tasks_gen/train/G1060/faulty/m3_i_air_quality.ddls.asddls create mode 100644 tasks_gen/train/G1060/faulty/m4_i_air_quality.ddls.asddls create mode 100644 tasks_gen/train/G1060/generation.json create mode 100644 tasks_gen/train/G1060/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1060/mutation.json create mode 100644 tasks_gen/train/G1060/reference/i_air_quality.ddls.asddls create mode 100644 tasks_gen/train/G1060/reference/t41_test.clas.abap create mode 100644 tasks_gen/train/G1060/seed/reading.tabl.asabap create mode 100644 tasks_gen/train/G1060/seed/station.tabl.asabap create mode 100644 tasks_gen/train/G1060/spec.md create mode 100644 tasks_gen/train/G1060/task.json create mode 100644 tasks_gen/train/G1061/faulty/m0_watch_order_pool.clas.abap create mode 100644 tasks_gen/train/G1061/faulty/m1_watch_order_pool.clas.abap create mode 100644 tasks_gen/train/G1061/faulty/m2_watch_order_pool.clas.abap create mode 100644 tasks_gen/train/G1061/generation.json create mode 100644 tasks_gen/train/G1061/hidden/watch_order_hidden.clas.abap create mode 100644 tasks_gen/train/G1061/mutation.json create mode 100644 tasks_gen/train/G1061/reference/watch_order_pool.clas.abap create mode 100644 tasks_gen/train/G1061/reference/watch_order_pool.testclasses.abap create mode 100644 tasks_gen/train/G1061/seed/if_watch_order.intf.abap create mode 100644 tasks_gen/train/G1061/spec.md create mode 100644 tasks_gen/train/G1061/task.json create mode 100644 tasks_gen/train/G1062/faulty/m0_parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/faulty/m1_parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/faulty/m2_parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/faulty/m3_parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/faulty/m4_parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/generation.json create mode 100644 tasks_gen/train/G1062/hidden/parking_hidden.clas.abap create mode 100644 tasks_gen/train/G1062/mutation.json create mode 100644 tasks_gen/train/G1062/reference/parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/reference/parking_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1062/seed/parking_pricer.clas.abap create mode 100644 tasks_gen/train/G1062/spec.md create mode 100644 tasks_gen/train/G1062/task.json create mode 100644 tasks_gen/train/G1063/faulty/m2_toll_pricer.clas.abap create mode 100644 tasks_gen/train/G1063/faulty/m3_toll_pricer.clas.abap create mode 100644 tasks_gen/train/G1063/faulty/m4_toll_pricer.clas.abap create mode 100644 tasks_gen/train/G1063/generation.json create mode 100644 tasks_gen/train/G1063/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1063/mutation.json create mode 100644 tasks_gen/train/G1063/reference/toll_pricer.clas.abap create mode 100644 tasks_gen/train/G1063/reference/toll_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1063/seed/if_toll_pricer.intf.abap create mode 100644 tasks_gen/train/G1063/spec.md create mode 100644 tasks_gen/train/G1063/task.json create mode 100644 tasks_gen/train/G1064/faulty/m1_care_visit_checker.clas.abap create mode 100644 tasks_gen/train/G1064/faulty/m2_care_visit_checker.clas.abap create mode 100644 tasks_gen/train/G1064/faulty/m3_care_visit_checker.clas.abap create mode 100644 tasks_gen/train/G1064/faulty/m4_care_visit_checker.clas.abap create mode 100644 tasks_gen/train/G1064/generation.json create mode 100644 tasks_gen/train/G1064/hidden/d01_hidden.clas.abap create mode 100644 tasks_gen/train/G1064/mutation.json create mode 100644 tasks_gen/train/G1064/reference/care_visit_checker.clas.abap create mode 100644 tasks_gen/train/G1064/reference/care_visit_checker.testclasses.abap create mode 100644 tasks_gen/train/G1064/seed/cx_care_visit.clas.abap create mode 100644 tasks_gen/train/G1064/seed/if_care_reason.intf.abap create mode 100644 tasks_gen/train/G1064/spec.md create mode 100644 tasks_gen/train/G1064/task.json create mode 100644 tasks_gen/train/G1065/faulty/m0_boat_hire_pricer.clas.abap create mode 100644 tasks_gen/train/G1065/faulty/m1_boat_hire_pricer.clas.abap create mode 100644 tasks_gen/train/G1065/faulty/m2_boat_hire_pricer.clas.abap create mode 100644 tasks_gen/train/G1065/faulty/m4_boat_hire_pricer.clas.abap create mode 100644 tasks_gen/train/G1065/generation.json create mode 100644 tasks_gen/train/G1065/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1065/mutation.json create mode 100644 tasks_gen/train/G1065/reference/boat_hire_pricer.clas.abap create mode 100644 tasks_gen/train/G1065/reference/boat_hire_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1065/seed/hire_rates.clas.abap create mode 100644 tasks_gen/train/G1065/spec.md create mode 100644 tasks_gen/train/G1065/task.json create mode 100644 tasks_gen/train/G1066/faulty/m0_lib_fee.func.abap create mode 100644 tasks_gen/train/G1066/faulty/m1_lib_fee.func.abap create mode 100644 tasks_gen/train/G1066/faulty/m2_lib_fee.func.abap create mode 100644 tasks_gen/train/G1066/faulty/m3_lib_fee.func.abap create mode 100644 tasks_gen/train/G1066/generation.json create mode 100644 tasks_gen/train/G1066/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1066/mutation.json create mode 100644 tasks_gen/train/G1066/reference/lib_fee.func.abap create mode 100644 tasks_gen/train/G1066/reference/t41_test.clas.abap create mode 100644 tasks_gen/train/G1066/seed/lib_sample.func.abap create mode 100644 tasks_gen/train/G1066/spec.md create mode 100644 tasks_gen/train/G1066/task.json create mode 100644 tasks_gen/train/G1067/generation.json create mode 100644 tasks_gen/train/G1067/reference/tent_hire_fee.clas.abap create mode 100644 tasks_gen/train/G1067/reference/tent_hire_fee.testclasses.abap create mode 100644 tasks_gen/train/G1067/seed/if_tent_hire.intf.abap create mode 100644 tasks_gen/train/G1067/spec.md create mode 100644 tasks_gen/train/G1067/task.json create mode 100644 tasks_gen/train/G1068/generation.json create mode 100644 tasks_gen/train/G1068/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1068/reference/i_hotel.ddls.asddls create mode 100644 tasks_gen/train/G1068/reference/i_stay.ddls.asddls create mode 100644 tasks_gen/train/G1068/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1068/seed/hotel.tabl.asabap create mode 100644 tasks_gen/train/G1068/seed/seed_data.clas.abap create mode 100644 tasks_gen/train/G1068/seed/stay.tabl.asabap create mode 100644 tasks_gen/train/G1068/spec.md create mode 100644 tasks_gen/train/G1068/task.json create mode 100644 tasks_gen/train/G1069/faulty/m0_league_table.clas.abap create mode 100644 tasks_gen/train/G1069/faulty/m1_league_table.clas.abap create mode 100644 tasks_gen/train/G1069/faulty/m2_league_table.clas.abap create mode 100644 tasks_gen/train/G1069/faulty/m3_league_table.clas.abap create mode 100644 tasks_gen/train/G1069/faulty/m4_league_table.clas.abap create mode 100644 tasks_gen/train/G1069/generation.json create mode 100644 tasks_gen/train/G1069/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1069/mutation.json create mode 100644 tasks_gen/train/G1069/reference/league_table.clas.abap create mode 100644 tasks_gen/train/G1069/reference/league_table.testclasses.abap create mode 100644 tasks_gen/train/G1069/spec.md create mode 100644 tasks_gen/train/G1069/task.json create mode 100644 tasks_gen/train/G1070/faulty/m0_shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/faulty/m1_shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/faulty/m2_shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/faulty/m3_shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/faulty/m4_shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/generation.json create mode 100644 tasks_gen/train/G1070/hidden/shred_hidden.clas.abap create mode 100644 tasks_gen/train/G1070/mutation.json create mode 100644 tasks_gen/train/G1070/reference/shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/reference/shred_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1070/seed/shred_pricer.clas.abap create mode 100644 tasks_gen/train/G1070/spec.md create mode 100644 tasks_gen/train/G1070/task.json create mode 100644 tasks_gen/train/G1071/faulty/m0_i_dive_stats.ddls.asddls create mode 100644 tasks_gen/train/G1071/faulty/m1_i_dive_stats.ddls.asddls create mode 100644 tasks_gen/train/G1071/faulty/m2_i_dive_stats.ddls.asddls create mode 100644 tasks_gen/train/G1071/generation.json create mode 100644 tasks_gen/train/G1071/hidden/t_dive_stats_hidden.clas.abap create mode 100644 tasks_gen/train/G1071/mutation.json create mode 100644 tasks_gen/train/G1071/reference/i_dive_stats.ddls.asddls create mode 100644 tasks_gen/train/G1071/reference/t_dive_stats_test.clas.abap create mode 100644 tasks_gen/train/G1071/seed/dive.tabl.asabap create mode 100644 tasks_gen/train/G1071/seed/diver.tabl.asabap create mode 100644 tasks_gen/train/G1071/seed/i_dive_stats.ddls.asddls create mode 100644 tasks_gen/train/G1071/seed/seed_dive.clas.abap create mode 100644 tasks_gen/train/G1071/seed/site.tabl.asabap create mode 100644 tasks_gen/train/G1071/spec.md create mode 100644 tasks_gen/train/G1071/task.json create mode 100644 tasks_gen/train/G1072/faulty/m1_i_guest_stay.ddls.asddls create mode 100644 tasks_gen/train/G1072/faulty/m2_i_guest_stay.ddls.asddls create mode 100644 tasks_gen/train/G1072/faulty/m3_i_guest_stay.ddls.asddls create mode 100644 tasks_gen/train/G1072/faulty/m4_i_guest_stay.ddls.asddls create mode 100644 tasks_gen/train/G1072/generation.json create mode 100644 tasks_gen/train/G1072/hidden/t30_hidden.clas.abap create mode 100644 tasks_gen/train/G1072/mutation.json create mode 100644 tasks_gen/train/G1072/reference/i_guest_stay.ddls.asddls create mode 100644 tasks_gen/train/G1072/reference/t30_test.clas.abap create mode 100644 tasks_gen/train/G1072/seed/guest.tabl.asabap create mode 100644 tasks_gen/train/G1072/seed/stay.tabl.asabap create mode 100644 tasks_gen/train/G1072/spec.md create mode 100644 tasks_gen/train/G1072/task.json create mode 100644 tasks_gen/train/G1073/generation.json create mode 100644 tasks_gen/train/G1073/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1073/reference/parking_eval.clas.abap create mode 100644 tasks_gen/train/G1073/reference/parking_eval.testclasses.abap create mode 100644 tasks_gen/train/G1073/seed/if_park_eval.intf.abap create mode 100644 tasks_gen/train/G1073/spec.md create mode 100644 tasks_gen/train/G1073/task.json create mode 100644 tasks_gen/train/G1074/faulty/m0_pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/faulty/m1_pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/faulty/m2_pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/faulty/m3_pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/faulty/m4_pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/generation.json create mode 100644 tasks_gen/train/G1074/hidden/pallet_hidden.clas.abap create mode 100644 tasks_gen/train/G1074/mutation.json create mode 100644 tasks_gen/train/G1074/reference/pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/reference/pallet_allocator.testclasses.abap create mode 100644 tasks_gen/train/G1074/seed/pallet_allocator.clas.abap create mode 100644 tasks_gen/train/G1074/spec.md create mode 100644 tasks_gen/train/G1074/task.json create mode 100644 tasks_gen/train/G1075/faulty/m0_childcare_pricer.clas.abap create mode 100644 tasks_gen/train/G1075/faulty/m1_childcare_pricer.clas.abap create mode 100644 tasks_gen/train/G1075/faulty/m2_childcare_pricer.clas.abap create mode 100644 tasks_gen/train/G1075/faulty/m3_childcare_pricer.clas.abap create mode 100644 tasks_gen/train/G1075/generation.json create mode 100644 tasks_gen/train/G1075/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1075/mutation.json create mode 100644 tasks_gen/train/G1075/reference/childcare_pricer.clas.abap create mode 100644 tasks_gen/train/G1075/reference/childcare_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1075/spec.md create mode 100644 tasks_gen/train/G1075/task.json create mode 100644 tasks_gen/train/G1076/faulty/m0_claim_checker.clas.abap create mode 100644 tasks_gen/train/G1076/faulty/m3_claim_checker.clas.abap create mode 100644 tasks_gen/train/G1076/faulty/m4_claim_checker.clas.abap create mode 100644 tasks_gen/train/G1076/generation.json create mode 100644 tasks_gen/train/G1076/hidden/claim_check_hidden.clas.abap create mode 100644 tasks_gen/train/G1076/mutation.json create mode 100644 tasks_gen/train/G1076/reference/claim_checker.clas.abap create mode 100644 tasks_gen/train/G1076/reference/claim_checker.testclasses.abap create mode 100644 tasks_gen/train/G1076/seed/cx_claim_error.clas.abap create mode 100644 tasks_gen/train/G1076/spec.md create mode 100644 tasks_gen/train/G1076/task.json create mode 100644 tasks_gen/train/G1077/faulty/m0_water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/faulty/m1_water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/faulty/m2_water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/faulty/m3_water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/faulty/m4_water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/generation.json create mode 100644 tasks_gen/train/G1077/hidden/water_bill_test.clas.abap create mode 100644 tasks_gen/train/G1077/mutation.json create mode 100644 tasks_gen/train/G1077/reference/water_bill.clas.abap create mode 100644 tasks_gen/train/G1077/reference/water_bill.testclasses.abap create mode 100644 tasks_gen/train/G1077/seed/water_tariff.clas.abap create mode 100644 tasks_gen/train/G1077/spec.md create mode 100644 tasks_gen/train/G1077/task.json create mode 100644 tasks_gen/train/G1078/faulty/m0_sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/faulty/m1_sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/faulty/m2_sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/faulty/m3_sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/faulty/m4_sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/generation.json create mode 100644 tasks_gen/train/G1078/hidden/sewer_hidden.clas.abap create mode 100644 tasks_gen/train/G1078/mutation.json create mode 100644 tasks_gen/train/G1078/reference/sewer_fee.func.abap create mode 100644 tasks_gen/train/G1078/reference/sewer_test.clas.abap create mode 100644 tasks_gen/train/G1078/seed/sewer_sample.func.abap create mode 100644 tasks_gen/train/G1078/spec.md create mode 100644 tasks_gen/train/G1078/task.json create mode 100644 tasks_gen/train/G1079/generation.json create mode 100644 tasks_gen/train/G1079/reference/soil_evaluator.clas.abap create mode 100644 tasks_gen/train/G1079/reference/soil_evaluator.testclasses.abap create mode 100644 tasks_gen/train/G1079/spec.md create mode 100644 tasks_gen/train/G1079/task.json create mode 100644 tasks_gen/train/G1080/faulty/m0_i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/faulty/m1_i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/faulty/m2_i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/faulty/m3_i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/faulty/m4_i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/generation.json create mode 100644 tasks_gen/train/G1080/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1080/mutation.json create mode 100644 tasks_gen/train/G1080/reference/i_vend_state.ddls.asddls create mode 100644 tasks_gen/train/G1080/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1080/seed/vmach.tabl.asabap create mode 100644 tasks_gen/train/G1080/seed/vslot.tabl.asabap create mode 100644 tasks_gen/train/G1080/spec.md create mode 100644 tasks_gen/train/G1080/task.json create mode 100644 tasks_gen/train/G1081/faulty/m0_studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/faulty/m1_studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/faulty/m2_studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/faulty/m3_studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/faulty/m4_studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/generation.json create mode 100644 tasks_gen/train/G1081/hidden/t30_hidden.clas.abap create mode 100644 tasks_gen/train/G1081/mutation.json create mode 100644 tasks_gen/train/G1081/reference/studio_summary.clas.abap create mode 100644 tasks_gen/train/G1081/reference/studio_summary.testclasses.abap create mode 100644 tasks_gen/train/G1081/spec.md create mode 100644 tasks_gen/train/G1081/task.json create mode 100644 tasks_gen/train/G1082/faulty/m0_climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/faulty/m1_climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/faulty/m2_climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/faulty/m3_climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/faulty/m4_climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/generation.json create mode 100644 tasks_gen/train/G1082/hidden/climb_hidden.clas.abap create mode 100644 tasks_gen/train/G1082/mutation.json create mode 100644 tasks_gen/train/G1082/reference/climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/reference/climb_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1082/seed/climb_pricer.clas.abap create mode 100644 tasks_gen/train/G1082/spec.md create mode 100644 tasks_gen/train/G1082/task.json create mode 100644 tasks_gen/train/G1083/faulty/m0_glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/faulty/m1_glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/faulty/m2_glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/faulty/m3_glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/faulty/m4_glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/generation.json create mode 100644 tasks_gen/train/G1083/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1083/mutation.json create mode 100644 tasks_gen/train/G1083/reference/glaz_calc.clas.abap create mode 100644 tasks_gen/train/G1083/reference/glaz_calc.clas.testclasses.abap create mode 100644 tasks_gen/train/G1083/reference/glaz_fee.func.abap create mode 100644 tasks_gen/train/G1083/reference/t31_test.clas.abap create mode 100644 tasks_gen/train/G1083/seed/glaz_sample.func.abap create mode 100644 tasks_gen/train/G1083/spec.md create mode 100644 tasks_gen/train/G1083/task.json create mode 100644 tasks_gen/train/G1084/faulty/m0_seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/faulty/m1_seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/faulty/m2_seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/faulty/m3_seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/faulty/m4_seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/generation.json create mode 100644 tasks_gen/train/G1084/hidden/seed_lot_hidden.clas.abap create mode 100644 tasks_gen/train/G1084/mutation.json create mode 100644 tasks_gen/train/G1084/reference/seed_lot_checker.clas.abap create mode 100644 tasks_gen/train/G1084/reference/seed_lot_checker.testclasses.abap create mode 100644 tasks_gen/train/G1084/seed/cx_lot_date.clas.abap create mode 100644 tasks_gen/train/G1084/seed/cx_lot_format.clas.abap create mode 100644 tasks_gen/train/G1084/seed/cx_lot_germination.clas.abap create mode 100644 tasks_gen/train/G1084/seed/cx_lot_moisture.clas.abap create mode 100644 tasks_gen/train/G1084/seed/cx_seed_lot.clas.abap create mode 100644 tasks_gen/train/G1084/spec.md create mode 100644 tasks_gen/train/G1084/task.json create mode 100644 tasks_gen/train/G1085/faulty/m0_dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/faulty/m1_dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/faulty/m2_dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/faulty/m3_dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/faulty/m4_dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/generation.json create mode 100644 tasks_gen/train/G1085/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1085/mutation.json create mode 100644 tasks_gen/train/G1085/reference/dlv_price.func.abap create mode 100644 tasks_gen/train/G1085/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1085/seed/dlv_sample.func.abap create mode 100644 tasks_gen/train/G1085/spec.md create mode 100644 tasks_gen/train/G1085/task.json create mode 100644 tasks_gen/train/G1086/faulty/m0_wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/faulty/m1_wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/faulty/m2_wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/faulty/m3_wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/faulty/m4_wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/generation.json create mode 100644 tasks_gen/train/G1086/hidden/wood_hidden.clas.abap create mode 100644 tasks_gen/train/G1086/mutation.json create mode 100644 tasks_gen/train/G1086/reference/wood_fee.func.abap create mode 100644 tasks_gen/train/G1086/reference/wood_test.clas.abap create mode 100644 tasks_gen/train/G1086/seed/wood_sample.func.abap create mode 100644 tasks_gen/train/G1086/spec.md create mode 100644 tasks_gen/train/G1086/task.json create mode 100644 tasks_gen/train/G1087/generation.json create mode 100644 tasks_gen/train/G1087/reference/tool_hire.clas.abap create mode 100644 tasks_gen/train/G1087/reference/tool_hire.testclasses.abap create mode 100644 tasks_gen/train/G1087/seed/if_tool_hire.intf.abap create mode 100644 tasks_gen/train/G1087/spec.md create mode 100644 tasks_gen/train/G1087/task.json create mode 100644 tasks_gen/train/G1089/faulty/m1_keg_tour_merger.clas.abap create mode 100644 tasks_gen/train/G1089/faulty/m2_keg_tour_merger.clas.abap create mode 100644 tasks_gen/train/G1089/faulty/m3_keg_tour_merger.clas.abap create mode 100644 tasks_gen/train/G1089/faulty/m4_keg_tour_merger.clas.abap create mode 100644 tasks_gen/train/G1089/generation.json create mode 100644 tasks_gen/train/G1089/hidden/keg_hidden.clas.abap create mode 100644 tasks_gen/train/G1089/mutation.json create mode 100644 tasks_gen/train/G1089/reference/keg_tour_merger.clas.abap create mode 100644 tasks_gen/train/G1089/reference/keg_tour_merger.testclasses.abap create mode 100644 tasks_gen/train/G1089/spec.md create mode 100644 tasks_gen/train/G1089/task.json create mode 100644 tasks_gen/train/G1090/faulty/m1_ski_lift_old.prog.abap create mode 100644 tasks_gen/train/G1090/faulty/m2_ski_lift_old.prog.abap create mode 100644 tasks_gen/train/G1090/generation.json create mode 100644 tasks_gen/train/G1090/hidden/ski_hidden.clas.abap create mode 100644 tasks_gen/train/G1090/mutation.json create mode 100644 tasks_gen/train/G1090/reference/ski_lift_old.prog.abap create mode 100644 tasks_gen/train/G1090/seed/ski_lift_old.prog.abap create mode 100644 tasks_gen/train/G1090/seed/ski_seed.clas.abap create mode 100644 tasks_gen/train/G1090/seed/skiride.tabl.asabap create mode 100644 tasks_gen/train/G1090/spec.md create mode 100644 tasks_gen/train/G1090/task.json create mode 100644 tasks_gen/train/G1091/faulty/m0_i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/faulty/m1_i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/faulty/m2_i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/faulty/m3_i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/faulty/m4_i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/generation.json create mode 100644 tasks_gen/train/G1091/hidden/tpo_hidden.clas.abap create mode 100644 tasks_gen/train/G1091/mutation.json create mode 100644 tasks_gen/train/G1091/reference/i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/reference/tpo_test.clas.abap create mode 100644 tasks_gen/train/G1091/seed/i_po_value.ddls.asddls create mode 100644 tasks_gen/train/G1091/seed/po_head.tabl.asabap create mode 100644 tasks_gen/train/G1091/seed/po_item.tabl.asabap create mode 100644 tasks_gen/train/G1091/spec.md create mode 100644 tasks_gen/train/G1091/task.json create mode 100644 tasks_gen/train/G1092/faulty/m0_i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/faulty/m1_i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/faulty/m2_i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/faulty/m3_i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/faulty/m4_i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/generation.json create mode 100644 tasks_gen/train/G1092/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1092/mutation.json create mode 100644 tasks_gen/train/G1092/reference/i_route_load.ddls.asddls create mode 100644 tasks_gen/train/G1092/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1092/seed/sail.tabl.asabap create mode 100644 tasks_gen/train/G1092/seed/veh.tabl.asabap create mode 100644 tasks_gen/train/G1092/spec.md create mode 100644 tasks_gen/train/G1092/task.json create mode 100644 tasks_gen/train/G1093/generation.json create mode 100644 tasks_gen/train/G1093/hidden/mix_hidden.clas.abap create mode 100644 tasks_gen/train/G1093/mutation.json create mode 100644 tasks_gen/train/G1093/reference/mix_price.func.abap create mode 100644 tasks_gen/train/G1093/reference/mix_test.clas.abap create mode 100644 tasks_gen/train/G1093/seed/mix_sample.func.abap create mode 100644 tasks_gen/train/G1093/seed/paint_mix.clas.abap create mode 100644 tasks_gen/train/G1093/seed/tintpr.tabl.abap create mode 100644 tasks_gen/train/G1093/spec.md create mode 100644 tasks_gen/train/G1093/task.json create mode 100644 tasks_gen/train/G1094/generation.json create mode 100644 tasks_gen/train/G1094/hidden/court_hidden.clas.abap create mode 100644 tasks_gen/train/G1094/mutation.json create mode 100644 tasks_gen/train/G1094/reference/court_old.prog.abap create mode 100644 tasks_gen/train/G1094/seed/booking.tabl.asabap create mode 100644 tasks_gen/train/G1094/seed/court.tabl.asabap create mode 100644 tasks_gen/train/G1094/seed/court_old.prog.abap create mode 100644 tasks_gen/train/G1094/seed/court_seed.clas.abap create mode 100644 tasks_gen/train/G1094/spec.md create mode 100644 tasks_gen/train/G1094/task.json create mode 100644 tasks_gen/train/G1095/faulty/m0_heli_pricer.clas.abap create mode 100644 tasks_gen/train/G1095/faulty/m1_heli_fee.func.abap create mode 100644 tasks_gen/train/G1095/faulty/m2_heli_pricer.clas.abap create mode 100644 tasks_gen/train/G1095/faulty/m3_heli_fee.func.abap create mode 100644 tasks_gen/train/G1095/faulty/m4_heli_pricer.clas.abap create mode 100644 tasks_gen/train/G1095/generation.json create mode 100644 tasks_gen/train/G1095/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1095/mutation.json create mode 100644 tasks_gen/train/G1095/reference/cx_heli_invalid.clas.abap create mode 100644 tasks_gen/train/G1095/reference/heli_fee.func.abap create mode 100644 tasks_gen/train/G1095/reference/heli_pricer.clas.abap create mode 100644 tasks_gen/train/G1095/reference/heli_pricer_testclasses.clas.abap create mode 100644 tasks_gen/train/G1095/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1095/seed/heli_fee.func.abap create mode 100644 tasks_gen/train/G1095/seed/heli_sample.func.abap create mode 100644 tasks_gen/train/G1095/spec.md create mode 100644 tasks_gen/train/G1095/task.json create mode 100644 tasks_gen/train/G1096/faulty/m0_adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/faulty/m1_adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/faulty/m2_adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/faulty/m3_adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/faulty/m4_adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/generation.json create mode 100644 tasks_gen/train/G1096/hidden/adopt_hidden.clas.abap create mode 100644 tasks_gen/train/G1096/mutation.json create mode 100644 tasks_gen/train/G1096/reference/adopt_review.func.abap create mode 100644 tasks_gen/train/G1096/reference/adopt_test.clas.abap create mode 100644 tasks_gen/train/G1096/reference/cx_adopt_reject.clas.abap create mode 100644 tasks_gen/train/G1096/seed/shelter_sample.func.abap create mode 100644 tasks_gen/train/G1096/spec.md create mode 100644 tasks_gen/train/G1096/task.json create mode 100644 tasks_gen/train/G1097/faulty/m0_weld_fee.func.abap create mode 100644 tasks_gen/train/G1097/faulty/m1_weld_fee.func.abap create mode 100644 tasks_gen/train/G1097/faulty/m2_weld_fee.func.abap create mode 100644 tasks_gen/train/G1097/faulty/m4_weld_fee.func.abap create mode 100644 tasks_gen/train/G1097/generation.json create mode 100644 tasks_gen/train/G1097/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1097/mutation.json create mode 100644 tasks_gen/train/G1097/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1097/reference/weld_fee.func.abap create mode 100644 tasks_gen/train/G1097/seed/weld_sample.func.abap create mode 100644 tasks_gen/train/G1097/spec.md create mode 100644 tasks_gen/train/G1097/task.json create mode 100644 tasks_gen/train/G1098/faulty/m0_drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/faulty/m1_drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/faulty/m2_drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/faulty/m3_drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/faulty/m4_drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/generation.json create mode 100644 tasks_gen/train/G1098/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1098/mutation.json create mode 100644 tasks_gen/train/G1098/reference/drv_fee.func.abap create mode 100644 tasks_gen/train/G1098/reference/t41_test.clas.abap create mode 100644 tasks_gen/train/G1098/seed/drv_sample.func.abap create mode 100644 tasks_gen/train/G1098/spec.md create mode 100644 tasks_gen/train/G1098/task.json create mode 100644 tasks_gen/train/G1099/generation.json create mode 100644 tasks_gen/train/G1099/reference/salon_pricer.clas.abap create mode 100644 tasks_gen/train/G1099/reference/salon_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1099/seed/if_salon_price.intf.abap create mode 100644 tasks_gen/train/G1099/spec.md create mode 100644 tasks_gen/train/G1099/task.json create mode 100644 tasks_gen/train/G1100/faulty/m0_i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/faulty/m1_i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/faulty/m2_i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/faulty/m3_i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/faulty/m4_i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/generation.json create mode 100644 tasks_gen/train/G1100/hidden/t42_hidden.clas.abap create mode 100644 tasks_gen/train/G1100/mutation.json create mode 100644 tasks_gen/train/G1100/reference/i_berth_fee.ddls.asddls create mode 100644 tasks_gen/train/G1100/reference/t42_test.clas.abap create mode 100644 tasks_gen/train/G1100/seed/berth.tabl.asabap create mode 100644 tasks_gen/train/G1100/seed/pier.tabl.asabap create mode 100644 tasks_gen/train/G1100/seed/seed_berth.clas.abap create mode 100644 tasks_gen/train/G1100/spec.md create mode 100644 tasks_gen/train/G1100/task.json create mode 100644 tasks_gen/train/G1101/generation.json create mode 100644 tasks_gen/train/G1101/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1101/mutation.json create mode 100644 tasks_gen/train/G1101/reference/banquet_fee.func.abap create mode 100644 tasks_gen/train/G1101/reference/t41_test.clas.abap create mode 100644 tasks_gen/train/G1101/seed/dish.tabl.abap create mode 100644 tasks_gen/train/G1101/seed/pos.tabl.abap create mode 100644 tasks_gen/train/G1101/seed/seed_banquet.clas.abap create mode 100644 tasks_gen/train/G1101/spec.md create mode 100644 tasks_gen/train/G1101/task.json create mode 100644 tasks_gen/train/G1102/generation.json create mode 100644 tasks_gen/train/G1102/hidden/shop_hidden.clas.abap create mode 100644 tasks_gen/train/G1102/reference/farm_shop_old.prog.abap create mode 100644 tasks_gen/train/G1102/seed/farm_shop_old.prog.abap create mode 100644 tasks_gen/train/G1102/seed/gdsale.tabl.asabap create mode 100644 tasks_gen/train/G1102/seed/goods.tabl.asabap create mode 100644 tasks_gen/train/G1102/seed/shop_seed.clas.abap create mode 100644 tasks_gen/train/G1102/spec.md create mode 100644 tasks_gen/train/G1102/task.json create mode 100644 tasks_gen/train/G1103/faulty/m0_wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/faulty/m1_wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/faulty/m2_wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/faulty/m3_wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/faulty/m4_wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/generation.json create mode 100644 tasks_gen/train/G1103/hidden/t37_hidden.clas.abap create mode 100644 tasks_gen/train/G1103/mutation.json create mode 100644 tasks_gen/train/G1103/reference/t37_test.clas.abap create mode 100644 tasks_gen/train/G1103/reference/wash_fee.func.abap create mode 100644 tasks_gen/train/G1103/seed/wash_sample.func.abap create mode 100644 tasks_gen/train/G1103/spec.md create mode 100644 tasks_gen/train/G1103/task.json create mode 100644 tasks_gen/train/G1104/faulty/m0_golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/faulty/m1_golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/faulty/m2_golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/faulty/m3_golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/faulty/m4_golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/generation.json create mode 100644 tasks_gen/train/G1104/hidden/t71_hidden.clas.abap create mode 100644 tasks_gen/train/G1104/mutation.json create mode 100644 tasks_gen/train/G1104/reference/cx_golf.clas.abap create mode 100644 tasks_gen/train/G1104/reference/golf_fee.func.abap create mode 100644 tasks_gen/train/G1104/reference/t71_test.clas.abap create mode 100644 tasks_gen/train/G1104/spec.md create mode 100644 tasks_gen/train/G1104/task.json create mode 100644 tasks_gen/train/G1105/faulty/m0_kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/faulty/m1_kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/faulty/m2_kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/faulty/m3_kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/faulty/m4_kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/generation.json create mode 100644 tasks_gen/train/G1105/hidden/t42_hidden.clas.abap create mode 100644 tasks_gen/train/G1105/mutation.json create mode 100644 tasks_gen/train/G1105/reference/kennel_fee.func.abap create mode 100644 tasks_gen/train/G1105/reference/t42_test.clas.abap create mode 100644 tasks_gen/train/G1105/seed/kennel_sample.func.abap create mode 100644 tasks_gen/train/G1105/spec.md create mode 100644 tasks_gen/train/G1105/task.json create mode 100644 tasks_gen/train/G1106/generation.json create mode 100644 tasks_gen/train/G1106/hidden/bake_hidden.clas.abap create mode 100644 tasks_gen/train/G1106/reference/bake_sales.prog.abap create mode 100644 tasks_gen/train/G1106/seed/bake_seed.clas.abap create mode 100644 tasks_gen/train/G1106/seed/deliv.tabl.asabap create mode 100644 tasks_gen/train/G1106/spec.md create mode 100644 tasks_gen/train/G1106/task.json create mode 100644 tasks_gen/train/G1107/generation.json create mode 100644 tasks_gen/train/G1107/reference/lkr_fee.func.abap create mode 100644 tasks_gen/train/G1107/reference/lkr_test.clas.abap create mode 100644 tasks_gen/train/G1107/seed/lkr_sample.func.abap create mode 100644 tasks_gen/train/G1107/spec.md create mode 100644 tasks_gen/train/G1107/task.json create mode 100644 tasks_gen/train/G1108/generation.json create mode 100644 tasks_gen/train/G1108/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1108/reference/i_lesson_instr.ddls.asddls create mode 100644 tasks_gen/train/G1108/reference/t31_test.clas.abap create mode 100644 tasks_gen/train/G1108/seed/instr.tabl.asabap create mode 100644 tasks_gen/train/G1108/seed/lesson.tabl.asabap create mode 100644 tasks_gen/train/G1108/spec.md create mode 100644 tasks_gen/train/G1108/task.json create mode 100644 tasks_gen/train/G1109/generation.json create mode 100644 tasks_gen/train/G1109/hidden/tel_hidden.clas.abap create mode 100644 tasks_gen/train/G1109/reference/tel_rate.func.abap create mode 100644 tasks_gen/train/G1109/reference/tel_test.clas.abap create mode 100644 tasks_gen/train/G1109/seed/tel_sample.func.abap create mode 100644 tasks_gen/train/G1109/spec.md create mode 100644 tasks_gen/train/G1109/task.json create mode 100644 tasks_gen/train/G1110/faulty/m1_scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/faulty/m2_scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/faulty/m3_scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/faulty/m4_scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/generation.json create mode 100644 tasks_gen/train/G1110/hidden/scrap_hidden.clas.abap create mode 100644 tasks_gen/train/G1110/mutation.json create mode 100644 tasks_gen/train/G1110/reference/scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/seed/scrap.tabl.asabap create mode 100644 tasks_gen/train/G1110/seed/scrap_pay.prog.abap create mode 100644 tasks_gen/train/G1110/seed/scrap_seed.clas.abap create mode 100644 tasks_gen/train/G1110/spec.md create mode 100644 tasks_gen/train/G1110/task.json create mode 100644 tasks_gen/train/G1111/faulty/m0_i_tour_load.ddls.asddls create mode 100644 tasks_gen/train/G1111/faulty/m1_i_tour_load.ddls.asddls create mode 100644 tasks_gen/train/G1111/faulty/m2_i_tour_load.ddls.asddls create mode 100644 tasks_gen/train/G1111/generation.json create mode 100644 tasks_gen/train/G1111/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1111/mutation.json create mode 100644 tasks_gen/train/G1111/reference/i_tour_load.ddls.asddls create mode 100644 tasks_gen/train/G1111/reference/t31_test.clas.abap create mode 100644 tasks_gen/train/G1111/seed/i_tour_load.ddls.asddls create mode 100644 tasks_gen/train/G1111/seed/parcel.tabl.asabap create mode 100644 tasks_gen/train/G1111/seed/tour.tabl.asabap create mode 100644 tasks_gen/train/G1111/spec.md create mode 100644 tasks_gen/train/G1111/task.json create mode 100644 tasks_gen/train/G1112/faulty/m0_i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/faulty/m1_i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/faulty/m2_i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/faulty/m3_i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/faulty/m4_i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/generation.json create mode 100644 tasks_gen/train/G1112/hidden/blood_hidden.clas.abap create mode 100644 tasks_gen/train/G1112/mutation.json create mode 100644 tasks_gen/train/G1112/reference/blood_test.clas.abap create mode 100644 tasks_gen/train/G1112/reference/i_blood_stock.ddls.asddls create mode 100644 tasks_gen/train/G1112/seed/camp.tabl.asabap create mode 100644 tasks_gen/train/G1112/seed/donat.tabl.asabap create mode 100644 tasks_gen/train/G1112/spec.md create mode 100644 tasks_gen/train/G1112/task.json create mode 100644 tasks_gen/train/G1113/generation.json create mode 100644 tasks_gen/train/G1113/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1113/reference/don_sum.func.abap create mode 100644 tasks_gen/train/G1113/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1113/seed/don_sample.func.abap create mode 100644 tasks_gen/train/G1113/spec.md create mode 100644 tasks_gen/train/G1113/task.json create mode 100644 tasks_gen/train/G1114/faulty/m0_bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/faulty/m1_bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/faulty/m2_bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/faulty/m3_bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/faulty/m4_bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/generation.json create mode 100644 tasks_gen/train/G1114/hidden/bakery_hidden.clas.abap create mode 100644 tasks_gen/train/G1114/mutation.json create mode 100644 tasks_gen/train/G1114/reference/bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/seed/bakery_bill_old.prog.abap create mode 100644 tasks_gen/train/G1114/seed/bakery_seed.clas.abap create mode 100644 tasks_gen/train/G1114/seed/deliv.tabl.asabap create mode 100644 tasks_gen/train/G1114/seed/shop.tabl.asabap create mode 100644 tasks_gen/train/G1114/spec.md create mode 100644 tasks_gen/train/G1114/task.json create mode 100644 tasks_gen/train/G1115/faulty/m0_trans_pricer.clas.abap create mode 100644 tasks_gen/train/G1115/faulty/m1_trans_fee.func.abap create mode 100644 tasks_gen/train/G1115/faulty/m2_trans_pricer.clas.abap create mode 100644 tasks_gen/train/G1115/faulty/m3_trans_fee.func.abap create mode 100644 tasks_gen/train/G1115/faulty/m4_trans_pricer.clas.abap create mode 100644 tasks_gen/train/G1115/generation.json create mode 100644 tasks_gen/train/G1115/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1115/mutation.json create mode 100644 tasks_gen/train/G1115/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1115/reference/trans_fee.func.abap create mode 100644 tasks_gen/train/G1115/reference/trans_pricer.clas.abap create mode 100644 tasks_gen/train/G1115/reference/trans_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1115/seed/trans_sample.func.abap create mode 100644 tasks_gen/train/G1115/spec.md create mode 100644 tasks_gen/train/G1115/task.json create mode 100644 tasks_gen/train/G1116/faulty/m0_carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/faulty/m1_carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/faulty/m2_carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/faulty/m3_carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/faulty/m4_carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/generation.json create mode 100644 tasks_gen/train/G1116/hidden/t42_hidden.clas.abap create mode 100644 tasks_gen/train/G1116/mutation.json create mode 100644 tasks_gen/train/G1116/reference/carpet_fee.func.abap create mode 100644 tasks_gen/train/G1116/reference/t42_test.clas.abap create mode 100644 tasks_gen/train/G1116/seed/carpet_sample.func.abap create mode 100644 tasks_gen/train/G1116/spec.md create mode 100644 tasks_gen/train/G1116/task.json create mode 100644 tasks_gen/train/G1117/generation.json create mode 100644 tasks_gen/train/G1117/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1117/mutation.json create mode 100644 tasks_gen/train/G1117/reference/parcel_fee.func.abap create mode 100644 tasks_gen/train/G1117/reference/t21_test.clas.abap create mode 100644 tasks_gen/train/G1117/seed/zone_lookup.func.abap create mode 100644 tasks_gen/train/G1117/spec.md create mode 100644 tasks_gen/train/G1117/task.json create mode 100644 tasks_gen/train/G1118/faulty/m0_subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/faulty/m1_subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/faulty/m2_subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/faulty/m3_subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/faulty/m4_subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/generation.json create mode 100644 tasks_gen/train/G1118/hidden/t15_hidden.clas.abap create mode 100644 tasks_gen/train/G1118/mutation.json create mode 100644 tasks_gen/train/G1118/reference/subs_sum.prog.abap create mode 100644 tasks_gen/train/G1118/seed/subs.tabl.asabap create mode 100644 tasks_gen/train/G1118/seed/t15_seed.clas.abap create mode 100644 tasks_gen/train/G1118/spec.md create mode 100644 tasks_gen/train/G1118/task.json create mode 100644 tasks_gen/train/G1119/generation.json create mode 100644 tasks_gen/train/G1119/reference/bind_fee.func.abap create mode 100644 tasks_gen/train/G1119/reference/bind_test.clas.abap create mode 100644 tasks_gen/train/G1119/seed/bind_sample.func.abap create mode 100644 tasks_gen/train/G1119/spec.md create mode 100644 tasks_gen/train/G1119/task.json create mode 100644 tasks_gen/train/G1120/faulty/m0_i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/faulty/m1_i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/faulty/m2_i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/faulty/m3_i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/faulty/m4_i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/generation.json create mode 100644 tasks_gen/train/G1120/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1120/mutation.json create mode 100644 tasks_gen/train/G1120/reference/i_brick_size.ddls.asddls create mode 100644 tasks_gen/train/G1120/reference/t31_test.clas.abap create mode 100644 tasks_gen/train/G1120/seed/brick.tabl.asabap create mode 100644 tasks_gen/train/G1120/spec.md create mode 100644 tasks_gen/train/G1120/task.json create mode 100644 tasks_gen/train/G1121/faulty/m0_mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/faulty/m1_mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/faulty/m2_mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/faulty/m3_mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/faulty/m4_mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/generation.json create mode 100644 tasks_gen/train/G1121/hidden/t_mush_hidden.clas.abap create mode 100644 tasks_gen/train/G1121/mutation.json create mode 100644 tasks_gen/train/G1121/reference/mush_harvest.func.abap create mode 100644 tasks_gen/train/G1121/reference/t_mush_test.clas.abap create mode 100644 tasks_gen/train/G1121/seed/harvest.tabl.abap create mode 100644 tasks_gen/train/G1121/spec.md create mode 100644 tasks_gen/train/G1121/task.json create mode 100644 tasks_gen/train/G1122/generation.json create mode 100644 tasks_gen/train/G1122/hidden/ext_hidden.clas.abap create mode 100644 tasks_gen/train/G1122/mutation.json create mode 100644 tasks_gen/train/G1122/reference/ext_check_old.prog.abap create mode 100644 tasks_gen/train/G1122/seed/ext_check_old.prog.abap create mode 100644 tasks_gen/train/G1122/seed/ext_seed.clas.abap create mode 100644 tasks_gen/train/G1122/seed/exting.tabl.asabap create mode 100644 tasks_gen/train/G1122/spec.md create mode 100644 tasks_gen/train/G1122/task.json create mode 100644 tasks_gen/train/G1123/generation.json create mode 100644 tasks_gen/train/G1123/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1123/reference/airport_fee.clas.abap create mode 100644 tasks_gen/train/G1123/reference/airport_fee.testclasses.abap create mode 100644 tasks_gen/train/G1123/spec.md create mode 100644 tasks_gen/train/G1123/task.json create mode 100644 tasks_gen/train/G1124/faulty/m0_shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/faulty/m1_shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/faulty/m2_shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/faulty/m3_shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/faulty/m4_shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/generation.json create mode 100644 tasks_gen/train/G1124/hidden/d02_hidden.clas.abap create mode 100644 tasks_gen/train/G1124/mutation.json create mode 100644 tasks_gen/train/G1124/reference/cx_shift_error.clas.abap create mode 100644 tasks_gen/train/G1124/reference/shift_booker.clas.abap create mode 100644 tasks_gen/train/G1124/reference/shift_booker.testclasses.abap create mode 100644 tasks_gen/train/G1124/spec.md create mode 100644 tasks_gen/train/G1124/task.json create mode 100644 tasks_gen/train/G1125/faulty/m0_prt_price.func.abap create mode 100644 tasks_gen/train/G1125/faulty/m2_prt_price.func.abap create mode 100644 tasks_gen/train/G1125/faulty/m3_prt_price.func.abap create mode 100644 tasks_gen/train/G1125/faulty/m4_prt_price.func.abap create mode 100644 tasks_gen/train/G1125/generation.json create mode 100644 tasks_gen/train/G1125/hidden/prt_hidden.clas.abap create mode 100644 tasks_gen/train/G1125/mutation.json create mode 100644 tasks_gen/train/G1125/reference/prt_price.func.abap create mode 100644 tasks_gen/train/G1125/reference/prt_test.clas.abap create mode 100644 tasks_gen/train/G1125/seed/prt_sample.func.abap create mode 100644 tasks_gen/train/G1125/spec.md create mode 100644 tasks_gen/train/G1125/task.json create mode 100644 tasks_gen/train/G1126/generation.json create mode 100644 tasks_gen/train/G1126/hidden/vet_hidden.clas.abap create mode 100644 tasks_gen/train/G1126/mutation.json create mode 100644 tasks_gen/train/G1126/reference/vet_sum.prog.abap create mode 100644 tasks_gen/train/G1126/seed/treat.tabl.asabap create mode 100644 tasks_gen/train/G1126/seed/vet_seed.clas.abap create mode 100644 tasks_gen/train/G1126/spec.md create mode 100644 tasks_gen/train/G1126/task.json create mode 100644 tasks_gen/train/G1127/generation.json create mode 100644 tasks_gen/train/G1127/reference/key_price.func.abap create mode 100644 tasks_gen/train/G1127/reference/key_test.clas.abap create mode 100644 tasks_gen/train/G1127/seed/key_sample.func.abap create mode 100644 tasks_gen/train/G1127/spec.md create mode 100644 tasks_gen/train/G1127/task.json create mode 100644 tasks_gen/train/G1128/generation.json create mode 100644 tasks_gen/train/G1128/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1128/mutation.json create mode 100644 tasks_gen/train/G1128/reference/i_route.ddls.asddls create mode 100644 tasks_gen/train/G1128/reference/i_stop.ddls.asddls create mode 100644 tasks_gen/train/G1128/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1128/seed/depot.tabl.asabap create mode 100644 tasks_gen/train/G1128/seed/route.tabl.asabap create mode 100644 tasks_gen/train/G1128/seed/stop.tabl.asabap create mode 100644 tasks_gen/train/G1128/spec.md create mode 100644 tasks_gen/train/G1128/task.json create mode 100644 tasks_gen/train/G1129/generation.json create mode 100644 tasks_gen/train/G1129/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1129/reference/meal_bill.func.abap create mode 100644 tasks_gen/train/G1129/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1129/seed/meal_data.clas.abap create mode 100644 tasks_gen/train/G1129/seed/mealbk.tabl.abap create mode 100644 tasks_gen/train/G1129/spec.md create mode 100644 tasks_gen/train/G1129/task.json create mode 100644 tasks_gen/train/G1130/faulty/m0_groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/faulty/m1_groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/faulty/m2_groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/faulty/m3_groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/faulty/m4_groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/generation.json create mode 100644 tasks_gen/train/G1130/hidden/groom_hidden.clas.abap create mode 100644 tasks_gen/train/G1130/mutation.json create mode 100644 tasks_gen/train/G1130/reference/groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/seed/groom.tabl.asabap create mode 100644 tasks_gen/train/G1130/seed/groom_bill_old.prog.abap create mode 100644 tasks_gen/train/G1130/seed/groom_seed.clas.abap create mode 100644 tasks_gen/train/G1130/spec.md create mode 100644 tasks_gen/train/G1130/task.json create mode 100644 tasks_gen/train/G1131/faulty/m0_i_honey_yield.ddls.asddls create mode 100644 tasks_gen/train/G1131/faulty/m1_i_honey_yield.ddls.asddls create mode 100644 tasks_gen/train/G1131/faulty/m2_i_honey_yield.ddls.asddls create mode 100644 tasks_gen/train/G1131/generation.json create mode 100644 tasks_gen/train/G1131/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1131/mutation.json create mode 100644 tasks_gen/train/G1131/reference/i_honey_yield.ddls.asddls create mode 100644 tasks_gen/train/G1131/reference/t41_test.clas.abap create mode 100644 tasks_gen/train/G1131/seed/apiary.tabl.asabap create mode 100644 tasks_gen/train/G1131/seed/honey.tabl.asabap create mode 100644 tasks_gen/train/G1131/seed/i_honey_yield.ddls.asddls create mode 100644 tasks_gen/train/G1131/spec.md create mode 100644 tasks_gen/train/G1131/task.json create mode 100644 tasks_gen/train/G1132/faulty/m0_walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/faulty/m1_walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/faulty/m2_walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/faulty/m3_walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/faulty/m4_walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/generation.json create mode 100644 tasks_gen/train/G1132/hidden/walk_check.clas.abap create mode 100644 tasks_gen/train/G1132/mutation.json create mode 100644 tasks_gen/train/G1132/reference/walker_stats.prog.abap create mode 100644 tasks_gen/train/G1132/seed/walk.tabl.asabap create mode 100644 tasks_gen/train/G1132/seed/walk_fill.clas.abap create mode 100644 tasks_gen/train/G1132/seed/walksum.tabl.asabap create mode 100644 tasks_gen/train/G1132/spec.md create mode 100644 tasks_gen/train/G1132/task.json create mode 100644 tasks_gen/train/G1133/faulty/m1_taxi_day.prog.abap create mode 100644 tasks_gen/train/G1133/faulty/m2_taxi_day.prog.abap create mode 100644 tasks_gen/train/G1133/faulty/m3_taxi_day.prog.abap create mode 100644 tasks_gen/train/G1133/faulty/m4_taxi_day.prog.abap create mode 100644 tasks_gen/train/G1133/generation.json create mode 100644 tasks_gen/train/G1133/hidden/taxi_hidden.clas.abap create mode 100644 tasks_gen/train/G1133/mutation.json create mode 100644 tasks_gen/train/G1133/reference/taxi_day.prog.abap create mode 100644 tasks_gen/train/G1133/seed/driver.tabl.asabap create mode 100644 tasks_gen/train/G1133/seed/taxi_seed.clas.abap create mode 100644 tasks_gen/train/G1133/seed/trip.tabl.asabap create mode 100644 tasks_gen/train/G1133/spec.md create mode 100644 tasks_gen/train/G1133/task.json create mode 100644 tasks_gen/train/G1135/faulty/m0_pool_ticket.clas.abap create mode 100644 tasks_gen/train/G1135/faulty/m2_pool_ticket.clas.abap create mode 100644 tasks_gen/train/G1135/faulty/m3_pool_ticket.clas.abap create mode 100644 tasks_gen/train/G1135/generation.json create mode 100644 tasks_gen/train/G1135/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1135/mutation.json create mode 100644 tasks_gen/train/G1135/reference/pool_ticket.clas.abap create mode 100644 tasks_gen/train/G1135/reference/pool_ticket.testclasses.abap create mode 100644 tasks_gen/train/G1135/spec.md create mode 100644 tasks_gen/train/G1135/task.json create mode 100644 tasks_gen/train/G1136/generation.json create mode 100644 tasks_gen/train/G1136/hidden/t_d_job_hidden.clas.abap create mode 100644 tasks_gen/train/G1136/reference/cx_job_error.clas.abap create mode 100644 tasks_gen/train/G1136/reference/job_planner.clas.abap create mode 100644 tasks_gen/train/G1136/reference/job_planner.testclasses.abap create mode 100644 tasks_gen/train/G1136/spec.md create mode 100644 tasks_gen/train/G1136/task.json create mode 100644 tasks_gen/train/G1137/faulty/m0_tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/faulty/m1_tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/faulty/m2_tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/faulty/m3_tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/faulty/m4_tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/generation.json create mode 100644 tasks_gen/train/G1137/hidden/t42_hidden.clas.abap create mode 100644 tasks_gen/train/G1137/mutation.json create mode 100644 tasks_gen/train/G1137/reference/t42_test.clas.abap create mode 100644 tasks_gen/train/G1137/reference/tattoo_fee.func.abap create mode 100644 tasks_gen/train/G1137/seed/tattoo_sample.func.abap create mode 100644 tasks_gen/train/G1137/spec.md create mode 100644 tasks_gen/train/G1137/task.json create mode 100644 tasks_gen/train/G1138/faulty/m0_line_output.prog.abap create mode 100644 tasks_gen/train/G1138/faulty/m1_line_output.prog.abap create mode 100644 tasks_gen/train/G1138/faulty/m2_line_output.prog.abap create mode 100644 tasks_gen/train/G1138/faulty/m3_line_output.prog.abap create mode 100644 tasks_gen/train/G1138/faulty/m4_line_output.prog.abap create mode 100644 tasks_gen/train/G1138/generation.json create mode 100644 tasks_gen/train/G1138/hidden/line_out_hid.clas.abap create mode 100644 tasks_gen/train/G1138/mutation.json create mode 100644 tasks_gen/train/G1138/reference/line_output.prog.abap create mode 100644 tasks_gen/train/G1138/seed/bottle.tabl.asabap create mode 100644 tasks_gen/train/G1138/seed/seed_bottle.clas.abap create mode 100644 tasks_gen/train/G1138/spec.md create mode 100644 tasks_gen/train/G1138/task.json create mode 100644 tasks_gen/train/G1139/generation.json create mode 100644 tasks_gen/train/G1139/reference/loan_check.func.abap create mode 100644 tasks_gen/train/G1139/reference/loan_test.clas.abap create mode 100644 tasks_gen/train/G1139/seed/loan_sample.func.abap create mode 100644 tasks_gen/train/G1139/spec.md create mode 100644 tasks_gen/train/G1139/task.json create mode 100644 tasks_gen/train/G1140/hidden/mill_hidden.clas.abap create mode 100644 tasks_gen/train/G1140/mutation.json create mode 100644 tasks_gen/train/G1140/reference/oil_report.prog.abap create mode 100644 tasks_gen/train/G1140/seed/grove.tabl.asabap create mode 100644 tasks_gen/train/G1140/seed/mill_seed.clas.abap create mode 100644 tasks_gen/train/G1140/seed/press.tabl.asabap create mode 100644 tasks_gen/train/G1140/spec.md create mode 100644 tasks_gen/train/G1140/task.json create mode 100644 tasks_gen/train/G1141/faulty/m1_exhib_cmp.prog.abap create mode 100644 tasks_gen/train/G1141/faulty/m2_exhib_cmp.prog.abap create mode 100644 tasks_gen/train/G1141/faulty/m3_exhib_cmp.prog.abap create mode 100644 tasks_gen/train/G1141/generation.json create mode 100644 tasks_gen/train/G1141/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1141/mutation.json create mode 100644 tasks_gen/train/G1141/reference/exhib_cmp.prog.abap create mode 100644 tasks_gen/train/G1141/seed/exhib.tabl.asabap create mode 100644 tasks_gen/train/G1141/seed/t31_seed.clas.abap create mode 100644 tasks_gen/train/G1141/seed/visit.tabl.asabap create mode 100644 tasks_gen/train/G1141/spec.md create mode 100644 tasks_gen/train/G1141/task.json create mode 100644 tasks_gen/train/G1142/generation.json create mode 100644 tasks_gen/train/G1142/hidden/hut_fee_hidden.clas.abap create mode 100644 tasks_gen/train/G1142/mutation.json create mode 100644 tasks_gen/train/G1142/reference/hut_fee.func.abap create mode 100644 tasks_gen/train/G1142/reference/hut_fee_test.clas.abap create mode 100644 tasks_gen/train/G1142/seed/hut_fee.func.abap create mode 100644 tasks_gen/train/G1142/spec.md create mode 100644 tasks_gen/train/G1142/task.json create mode 100644 tasks_gen/train/G1144/faulty/m0_move_fee.func.abap create mode 100644 tasks_gen/train/G1144/faulty/m1_move_fee.func.abap create mode 100644 tasks_gen/train/G1144/faulty/m2_move_fee.func.abap create mode 100644 tasks_gen/train/G1144/faulty/m3_move_fee.func.abap create mode 100644 tasks_gen/train/G1144/faulty/m4_move_fee.func.abap create mode 100644 tasks_gen/train/G1144/generation.json create mode 100644 tasks_gen/train/G1144/hidden/move_hidden.clas.abap create mode 100644 tasks_gen/train/G1144/mutation.json create mode 100644 tasks_gen/train/G1144/reference/move_fee.func.abap create mode 100644 tasks_gen/train/G1144/reference/move_test.clas.abap create mode 100644 tasks_gen/train/G1144/seed/move_sample.func.abap create mode 100644 tasks_gen/train/G1144/spec.md create mode 100644 tasks_gen/train/G1144/task.json create mode 100644 tasks_gen/train/G1145/faulty/m0_i_still_output.ddls.asddls create mode 100644 tasks_gen/train/G1145/faulty/m1_i_still_output.ddls.asddls create mode 100644 tasks_gen/train/G1145/faulty/m2_i_still_output.ddls.asddls create mode 100644 tasks_gen/train/G1145/generation.json create mode 100644 tasks_gen/train/G1145/hidden/t_runlog_hidden.clas.abap create mode 100644 tasks_gen/train/G1145/mutation.json create mode 100644 tasks_gen/train/G1145/reference/i_still_output.ddls.asddls create mode 100644 tasks_gen/train/G1145/reference/runlog_out_test.clas.abap create mode 100644 tasks_gen/train/G1145/seed/runlog.tabl.asabap create mode 100644 tasks_gen/train/G1145/seed/seed_runlog.clas.abap create mode 100644 tasks_gen/train/G1145/spec.md create mode 100644 tasks_gen/train/G1145/task.json create mode 100644 tasks_gen/train/G1147/generation.json create mode 100644 tasks_gen/train/G1147/reference/charge_stat.prog.abap create mode 100644 tasks_gen/train/G1147/seed/charge.tabl.asabap create mode 100644 tasks_gen/train/G1147/seed/charge_seed.clas.abap create mode 100644 tasks_gen/train/G1147/spec.md create mode 100644 tasks_gen/train/G1147/task.json create mode 100644 tasks_gen/train/G1148/faulty/m0_studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/faulty/m1_studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/faulty/m2_studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/faulty/m3_studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/faulty/m4_studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/generation.json create mode 100644 tasks_gen/train/G1148/hidden/t15_hidden.clas.abap create mode 100644 tasks_gen/train/G1148/mutation.json create mode 100644 tasks_gen/train/G1148/reference/studio_bill.prog.abap create mode 100644 tasks_gen/train/G1148/seed/studio.tabl.asabap create mode 100644 tasks_gen/train/G1148/seed/t15_seed.clas.abap create mode 100644 tasks_gen/train/G1148/spec.md create mode 100644 tasks_gen/train/G1148/task.json create mode 100644 tasks_gen/train/G1150/faulty/m0_farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/faulty/m1_farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/faulty/m2_farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/faulty/m3_farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/faulty/m4_farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/generation.json create mode 100644 tasks_gen/train/G1150/hidden/farrier_hidden.clas.abap create mode 100644 tasks_gen/train/G1150/mutation.json create mode 100644 tasks_gen/train/G1150/reference/farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/reference/farrier_test.clas.abap create mode 100644 tasks_gen/train/G1150/seed/farrier_fee.func.abap create mode 100644 tasks_gen/train/G1150/spec.md create mode 100644 tasks_gen/train/G1150/task.json create mode 100644 tasks_gen/train/G1153/generation.json create mode 100644 tasks_gen/train/G1153/hidden/dealer_hidden.clas.abap create mode 100644 tasks_gen/train/G1153/mutation.json create mode 100644 tasks_gen/train/G1153/reference/dealer_sum.prog.abap create mode 100644 tasks_gen/train/G1153/seed/dealer.tabl.asabap create mode 100644 tasks_gen/train/G1153/seed/dealer_seed.clas.abap create mode 100644 tasks_gen/train/G1153/seed/vsale.tabl.asabap create mode 100644 tasks_gen/train/G1153/spec.md create mode 100644 tasks_gen/train/G1153/task.json create mode 100644 tasks_gen/train/_logs/G1042.json create mode 100644 tasks_gen/train/_logs/G1044.json create mode 100644 tasks_gen/train/_logs/G1045.json create mode 100644 tasks_gen/train/_logs/G1047.json create mode 100644 tasks_gen/train/_logs/G1048.json create mode 100644 tasks_gen/train/_logs/G1050.json create mode 100644 tasks_gen/train/_logs/G1051.json create mode 100644 tasks_gen/train/_logs/G1053.json create mode 100644 tasks_gen/train/_logs/G1054.json create mode 100644 tasks_gen/train/_logs/G1056.json create mode 100644 tasks_gen/train/_logs/G1057.json create mode 100644 tasks_gen/train/_logs/G1058.json create mode 100644 tasks_gen/train/_logs/G1059.json create mode 100644 tasks_gen/train/_logs/G1060.json create mode 100644 tasks_gen/train/_logs/G1061.json create mode 100644 tasks_gen/train/_logs/G1062.json create mode 100644 tasks_gen/train/_logs/G1063.json create mode 100644 tasks_gen/train/_logs/G1064.json create mode 100644 tasks_gen/train/_logs/G1065.json create mode 100644 tasks_gen/train/_logs/G1066.json create mode 100644 tasks_gen/train/_logs/G1067.json create mode 100644 tasks_gen/train/_logs/G1068.json create mode 100644 tasks_gen/train/_logs/G1069.json create mode 100644 tasks_gen/train/_logs/G1070.json create mode 100644 tasks_gen/train/_logs/G1071.json create mode 100644 tasks_gen/train/_logs/G1072.json create mode 100644 tasks_gen/train/_logs/G1073.json create mode 100644 tasks_gen/train/_logs/G1074.json create mode 100644 tasks_gen/train/_logs/G1075.json create mode 100644 tasks_gen/train/_logs/G1076.json create mode 100644 tasks_gen/train/_logs/G1077.json create mode 100644 tasks_gen/train/_logs/G1078.json create mode 100644 tasks_gen/train/_logs/G1079.json create mode 100644 tasks_gen/train/_logs/G1080.json create mode 100644 tasks_gen/train/_logs/G1081.json create mode 100644 tasks_gen/train/_logs/G1082.json create mode 100644 tasks_gen/train/_logs/G1083.json create mode 100644 tasks_gen/train/_logs/G1084.json create mode 100644 tasks_gen/train/_logs/G1085.json create mode 100644 tasks_gen/train/_logs/G1086.json create mode 100644 tasks_gen/train/_logs/G1087.json create mode 100644 tasks_gen/train/_logs/G1088.json create mode 100644 tasks_gen/train/_logs/G1089.json create mode 100644 tasks_gen/train/_logs/G1090.json create mode 100644 tasks_gen/train/_logs/G1091.json create mode 100644 tasks_gen/train/_logs/G1092.json create mode 100644 tasks_gen/train/_logs/G1093.json create mode 100644 tasks_gen/train/_logs/G1094.json create mode 100644 tasks_gen/train/_logs/G1095.json create mode 100644 tasks_gen/train/_logs/G1096.json create mode 100644 tasks_gen/train/_logs/G1097.json create mode 100644 tasks_gen/train/_logs/G1098.json create mode 100644 tasks_gen/train/_logs/G1099.json create mode 100644 tasks_gen/train/_logs/G1100.json create mode 100644 tasks_gen/train/_logs/G1101.json create mode 100644 tasks_gen/train/_logs/G1102.json create mode 100644 tasks_gen/train/_logs/G1103.json create mode 100644 tasks_gen/train/_logs/G1104.json create mode 100644 tasks_gen/train/_logs/G1105.json create mode 100644 tasks_gen/train/_logs/G1106.json create mode 100644 tasks_gen/train/_logs/G1107.json create mode 100644 tasks_gen/train/_logs/G1108.json create mode 100644 tasks_gen/train/_logs/G1109.json create mode 100644 tasks_gen/train/_logs/G1110.json create mode 100644 tasks_gen/train/_logs/G1111.json create mode 100644 tasks_gen/train/_logs/G1112.json create mode 100644 tasks_gen/train/_logs/G1113.json create mode 100644 tasks_gen/train/_logs/G1114.json create mode 100644 tasks_gen/train/_logs/G1115.json create mode 100644 tasks_gen/train/_logs/G1116.json create mode 100644 tasks_gen/train/_logs/G1117.json create mode 100644 tasks_gen/train/_logs/G1118.json create mode 100644 tasks_gen/train/_logs/G1119.json create mode 100644 tasks_gen/train/_logs/G1120.json create mode 100644 tasks_gen/train/_logs/G1121.json create mode 100644 tasks_gen/train/_logs/G1122.json create mode 100644 tasks_gen/train/_logs/G1123.json create mode 100644 tasks_gen/train/_logs/G1124.json create mode 100644 tasks_gen/train/_logs/G1125.json create mode 100644 tasks_gen/train/_logs/G1126.json create mode 100644 tasks_gen/train/_logs/G1127.json create mode 100644 tasks_gen/train/_logs/G1128.json create mode 100644 tasks_gen/train/_logs/G1129.json create mode 100644 tasks_gen/train/_logs/G1130.json create mode 100644 tasks_gen/train/_logs/G1131.json create mode 100644 tasks_gen/train/_logs/G1132.json create mode 100644 tasks_gen/train/_logs/G1133.json create mode 100644 tasks_gen/train/_logs/G1134.json create mode 100644 tasks_gen/train/_logs/G1135.json create mode 100644 tasks_gen/train/_logs/G1136.json create mode 100644 tasks_gen/train/_logs/G1137.json create mode 100644 tasks_gen/train/_logs/G1138.json create mode 100644 tasks_gen/train/_logs/G1139.json create mode 100644 tasks_gen/train/_logs/G1141.json create mode 100644 tasks_gen/train/_logs/G1142.json create mode 100644 tasks_gen/train/_logs/G1144.json create mode 100644 tasks_gen/train/_logs/G1145.json create mode 100644 tasks_gen/train/_logs/G1147.json create mode 100644 tasks_gen/train/_logs/G1148.json create mode 100644 tasks_gen/train/_logs/G1150.json create mode 100644 tasks_gen/train/_logs/G1153.json diff --git a/docs/yol-haritasi.md b/docs/yol-haritasi.md index 3e21c8f..15f772e 100644 --- a/docs/yol-haritasi.md +++ b/docs/yol-haritasi.md @@ -134,6 +134,18 @@ Durum (2026-10-05): 3.1 eğitim modu hazır (`harness/trainset.py`, havuz `tasks +### Stage 2 summary at 50 accepted trajectories (2026-10-05 18:02) + +- Tasks: 139 accepted of 166 generated (K variants 18); trajectory runs 59, accepted trajectories 50 (acceptance 85%). +- Budget: used 10.2 usage (ledger 27.5) since 2026-10-05; left to the guard 14.6 usage (ledger 39.5; reserve 8 ledger kept); allowance until 11 October 24.8 usage. +- Repair share (accepted trajectories with an error followed by a fix): 32/50 = 64%. +- By category, accepted/runs: A 7/7 (repair 4); B 5/8 (repair 5); C 7/8 (repair 6); D 6/6 (repair 3); E 9/10 (repair 6); F 6/6 (repair 2); G 6/6 (repair 5); H 3/5 (repair 0); I 1/3 (repair 1). +- By object type, accepted/runs: CLAS 38/42 (repair 21); DDLS 6/11 (repair 6); FUNC 6/6 (repair 5). +- Tokens of accepted samples (20 tool schemas kept): p50 20147, p90 39458, p95 47694, max 71791, n 50. +- Syntax hints (proxy syntaxCheck added): 1 in 1 runs. +- Harness events: 2 ({'text:[LOCK]': 1, 'teardown': 1}); trajectory workers now 1. + + | Alt adım | İş | |---|---| | 3.1 | Görev üreteci: eval görevleriyle aynı şemada binlerce sentetik görev. Eval görevleriyle çakışma yok. Girdilerin bir kısmı serbest metin; yörüngelerin az bir kısmı farklı tool isim/şemalarıyla. | diff --git a/tasks_gen/train/G1042/faulty/m0_snow_fee.clas.abap b/tasks_gen/train/G1042/faulty/m0_snow_fee.clas.abap new file mode 100644 index 0000000..48e0115 --- /dev/null +++ b/tasks_gen/train/G1042/faulty/m0_snow_fee.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_area TYPE i. + TYPES ty_priority TYPE c LENGTH 1. + TYPES ty_distance TYPE i. + TYPES ty_fee TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. + + METHODS base_fee + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS rate_per_sqm + IMPORTING iv_area TYPE ty_area + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS surcharge_factor + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_factor) TYPE i. + METHODS travel_cost + IMPORTING iv_distance TYPE ty_distance + RETURNING VALUE(rv_cost) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}snow_fee IMPLEMENTATION. + METHOD calculate. + IF iv_area > 0. + rv_fee = 0. + RETURN. + ENDIF. + rv_fee = base_fee( iv_area = iv_area iv_priority = iv_priority ) + + travel_cost( iv_distance = iv_distance ). + ENDMETHOD. + + METHOD base_fee. + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area * rate_per_sqm( iv_area ). + IF lv_amount < c_minimum_fee. + lv_amount = c_minimum_fee. + ENDIF. + rv_fee = lv_amount * surcharge_factor( iv_priority ) / 100. + ENDMETHOD. + + METHOD rate_per_sqm. + IF iv_area <= c_threshold_small. + rv_rate = c_rate_small. + ELSEIF iv_area <= c_threshold_medium. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + ENDMETHOD. + + METHOD surcharge_factor. + CASE iv_priority. + WHEN c_priority_expr. + rv_factor = c_factor_express. + WHEN c_priority_emerg. + rv_factor = c_factor_emergency. + WHEN OTHERS. + rv_factor = c_factor_standard. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance - c_free_km. + IF lv_chargeable_km > 0. + rv_cost = lv_chargeable_km * c_rate_per_km. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/faulty/m1_snow_fee.clas.abap b/tasks_gen/train/G1042/faulty/m1_snow_fee.clas.abap new file mode 100644 index 0000000..ccbb6f0 --- /dev/null +++ b/tasks_gen/train/G1042/faulty/m1_snow_fee.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_area TYPE i. + TYPES ty_priority TYPE c LENGTH 1. + TYPES ty_distance TYPE i. + TYPES ty_fee TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. + + METHODS base_fee + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS rate_per_sqm + IMPORTING iv_area TYPE ty_area + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS surcharge_factor + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_factor) TYPE i. + METHODS travel_cost + IMPORTING iv_distance TYPE ty_distance + RETURNING VALUE(rv_cost) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}snow_fee IMPLEMENTATION. + METHOD calculate. + IF iv_area <= 0. + rv_fee = 0. + RETURN. + ENDIF. + rv_fee = base_fee( iv_area = iv_area iv_priority = iv_priority ) + - travel_cost( iv_distance = iv_distance ). + ENDMETHOD. + + METHOD base_fee. + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area * rate_per_sqm( iv_area ). + IF lv_amount < c_minimum_fee. + lv_amount = c_minimum_fee. + ENDIF. + rv_fee = lv_amount * surcharge_factor( iv_priority ) / 100. + ENDMETHOD. + + METHOD rate_per_sqm. + IF iv_area <= c_threshold_small. + rv_rate = c_rate_small. + ELSEIF iv_area <= c_threshold_medium. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + ENDMETHOD. + + METHOD surcharge_factor. + CASE iv_priority. + WHEN c_priority_expr. + rv_factor = c_factor_express. + WHEN c_priority_emerg. + rv_factor = c_factor_emergency. + WHEN OTHERS. + rv_factor = c_factor_standard. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance - c_free_km. + IF lv_chargeable_km > 0. + rv_cost = lv_chargeable_km * c_rate_per_km. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/faulty/m2_snow_fee.clas.abap b/tasks_gen/train/G1042/faulty/m2_snow_fee.clas.abap new file mode 100644 index 0000000..ceb65cd --- /dev/null +++ b/tasks_gen/train/G1042/faulty/m2_snow_fee.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_area TYPE i. + TYPES ty_priority TYPE c LENGTH 1. + TYPES ty_distance TYPE i. + TYPES ty_fee TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. + + METHODS base_fee + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS rate_per_sqm + IMPORTING iv_area TYPE ty_area + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS surcharge_factor + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_factor) TYPE i. + METHODS travel_cost + IMPORTING iv_distance TYPE ty_distance + RETURNING VALUE(rv_cost) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}snow_fee IMPLEMENTATION. + METHOD calculate. + IF iv_area <= 0. + rv_fee = 0. + RETURN. + ENDIF. + rv_fee = base_fee( iv_area = iv_area iv_priority = iv_priority ) + + travel_cost( iv_distance = iv_distance ). + ENDMETHOD. + + METHOD base_fee. + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area / rate_per_sqm( iv_area ). + IF lv_amount < c_minimum_fee. + lv_amount = c_minimum_fee. + ENDIF. + rv_fee = lv_amount * surcharge_factor( iv_priority ) / 100. + ENDMETHOD. + + METHOD rate_per_sqm. + IF iv_area <= c_threshold_small. + rv_rate = c_rate_small. + ELSEIF iv_area <= c_threshold_medium. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + ENDMETHOD. + + METHOD surcharge_factor. + CASE iv_priority. + WHEN c_priority_expr. + rv_factor = c_factor_express. + WHEN c_priority_emerg. + rv_factor = c_factor_emergency. + WHEN OTHERS. + rv_factor = c_factor_standard. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance - c_free_km. + IF lv_chargeable_km > 0. + rv_cost = lv_chargeable_km * c_rate_per_km. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/faulty/m3_snow_fee.clas.abap b/tasks_gen/train/G1042/faulty/m3_snow_fee.clas.abap new file mode 100644 index 0000000..4a51b40 --- /dev/null +++ b/tasks_gen/train/G1042/faulty/m3_snow_fee.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_area TYPE i. + TYPES ty_priority TYPE c LENGTH 1. + TYPES ty_distance TYPE i. + TYPES ty_fee TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. + + METHODS base_fee + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS rate_per_sqm + IMPORTING iv_area TYPE ty_area + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS surcharge_factor + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_factor) TYPE i. + METHODS travel_cost + IMPORTING iv_distance TYPE ty_distance + RETURNING VALUE(rv_cost) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}snow_fee IMPLEMENTATION. + METHOD calculate. + IF iv_area <= 0. + rv_fee = 0. + RETURN. + ENDIF. + rv_fee = base_fee( iv_area = iv_area iv_priority = iv_priority ) + + travel_cost( iv_distance = iv_distance ). + ENDMETHOD. + + METHOD base_fee. + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area * rate_per_sqm( iv_area ). + IF lv_amount < c_minimum_fee. + lv_amount = c_minimum_fee. + ENDIF. + rv_fee = lv_amount * surcharge_factor( iv_priority ) / 100. + ENDMETHOD. + + METHOD rate_per_sqm. + IF iv_area <= c_threshold_small. + rv_rate = c_rate_small. + ELSEIF iv_area > c_threshold_medium. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + ENDMETHOD. + + METHOD surcharge_factor. + CASE iv_priority. + WHEN c_priority_expr. + rv_factor = c_factor_express. + WHEN c_priority_emerg. + rv_factor = c_factor_emergency. + WHEN OTHERS. + rv_factor = c_factor_standard. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance - c_free_km. + IF lv_chargeable_km > 0. + rv_cost = lv_chargeable_km * c_rate_per_km. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/faulty/m4_snow_fee.clas.abap b/tasks_gen/train/G1042/faulty/m4_snow_fee.clas.abap new file mode 100644 index 0000000..7d7599b --- /dev/null +++ b/tasks_gen/train/G1042/faulty/m4_snow_fee.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_area TYPE i. + TYPES ty_priority TYPE c LENGTH 1. + TYPES ty_distance TYPE i. + TYPES ty_fee TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. + + METHODS base_fee + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS rate_per_sqm + IMPORTING iv_area TYPE ty_area + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS surcharge_factor + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_factor) TYPE i. + METHODS travel_cost + IMPORTING iv_distance TYPE ty_distance + RETURNING VALUE(rv_cost) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}snow_fee IMPLEMENTATION. + METHOD calculate. + IF iv_area <= 0. + rv_fee = 0. + RETURN. + ENDIF. + rv_fee = base_fee( iv_area = iv_area iv_priority = iv_priority ) + + travel_cost( iv_distance = iv_distance ). + ENDMETHOD. + + METHOD base_fee. + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area * rate_per_sqm( iv_area ). + IF lv_amount < c_minimum_fee. + lv_amount = c_minimum_fee. + ENDIF. + rv_fee = lv_amount * surcharge_factor( iv_priority ) / 100. + ENDMETHOD. + + METHOD rate_per_sqm. + IF iv_area <= c_threshold_small. + rv_rate = c_rate_small. + ELSEIF iv_area <= c_threshold_medium. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + ENDMETHOD. + + METHOD surcharge_factor. + CASE iv_priority. + WHEN c_priority_expr. + rv_factor = c_factor_express. + WHEN c_priority_emerg. + rv_factor = c_factor_emergency. + WHEN OTHERS. + rv_factor = c_factor_standard. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance + c_free_km. + IF lv_chargeable_km > 0. + rv_cost = lv_chargeable_km * c_rate_per_km. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/generation.json b/tasks_gen/train/G1042/generation.json new file mode 100644 index 0000000..2e8df89 --- /dev/null +++ b/tasks_gen/train/G1042/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1042", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap b/tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap index a8fe40c..820320f 100644 --- a/tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap +++ b/tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap @@ -12,8 +12,8 @@ CLASS {{p}}snow_fee_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC METHODS minimum_charge FOR TESTING. METHODS small_area_rate FOR TESTING. - METHODS mid_area_rate FOR TESTING. - METHODS mid_area_upper FOR TESTING. + METHODS medium_area_rate FOR TESTING. + METHODS medium_area_upper FOR TESTING. METHODS large_area_rate FOR TESTING. METHODS express_surcharge FOR TESTING. METHODS emergency_surcharge FOR TESTING. @@ -31,10 +31,8 @@ CLASS {{p}}snow_fee_hidden IMPLEMENTATION. ENDMETHOD. METHOD assert_fee. - DATA lv_expected TYPE {{p}}snow_fee=>ty_fee. - lv_expected = iv_expected. cl_abap_unit_assert=>assert_equals( - exp = lv_expected + exp = iv_expected act = mo_cut->calculate( iv_area = iv_area iv_priority = iv_priority iv_distance = iv_distance ) ). @@ -48,16 +46,16 @@ CLASS {{p}}snow_fee_hidden IMPLEMENTATION. assert_fee( iv_area = 500 iv_priority = 'S' iv_distance = 0 iv_expected = '100.00' ). ENDMETHOD. - METHOD mid_area_rate. - assert_fee( iv_area = 501 iv_priority = 'S' iv_distance = 0 iv_expected = '80.16' ). + METHOD medium_area_rate. + assert_fee( iv_area = 625 iv_priority = 'S' iv_distance = 0 iv_expected = '100.00' ). ENDMETHOD. - METHOD mid_area_upper. + METHOD medium_area_upper. assert_fee( iv_area = 2000 iv_priority = 'S' iv_distance = 0 iv_expected = '320.00' ). ENDMETHOD. METHOD large_area_rate. - assert_fee( iv_area = 2001 iv_priority = 'S' iv_distance = 0 iv_expected = '240.12' ). + assert_fee( iv_area = 2500 iv_priority = 'S' iv_distance = 0 iv_expected = '300.00' ). ENDMETHOD. METHOD express_surcharge. diff --git a/tasks_gen/train/G1042/mutation.json b/tasks_gen/train/G1042/mutation.json new file mode 100644 index 0000000..5a8d1a5 --- /dev/null +++ b/tasks_gen/train/G1042/mutation.json @@ -0,0 +1,93 @@ +{ + "task": "G1042", + "mutants": [ + { + "object": "{{P}}SNOW_FEE", + "mutant": "line 48: <= -> > (rel)", + "status": "killed", + "hidden": "0/12", + "failed_tests": [ + "EMERGENCY_SURCHARGE", + "EXPRESS_SURCHARGE", + "LARGE_AREA_RATE", + "MEDIUM_AREA_RATE", + "MEDIUM_AREA_UPPER", + "MINIMUM_CHARGE", + "MINIMUM_WITH_EXPRESS", + "SMALL_AREA_RATE", + "TRAVEL_CHARGED", + "TRAVEL_NOT_SURCHARGED", + "UNKNOWN_PRIORITY", + "ZERO_AREA_RETURNS_ZERO" + ] + }, + { + "object": "{{P}}SNOW_FEE", + "mutant": "line 53: + -> - (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "TRAVEL_CHARGED", + "TRAVEL_NOT_SURCHARGED" + ] + }, + { + "object": "{{P}}SNOW_FEE", + "mutant": "line 58: * -> / (arith)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "EMERGENCY_SURCHARGE", + "EXPRESS_SURCHARGE", + "LARGE_AREA_RATE", + "MEDIUM_AREA_RATE", + "MEDIUM_AREA_UPPER", + "MINIMUM_CHARGE", + "MINIMUM_WITH_EXPRESS", + "SMALL_AREA_RATE", + "TRAVEL_CHARGED", + "TRAVEL_NOT_SURCHARGED", + "UNKNOWN_PRIORITY" + ] + }, + { + "object": "{{P}}SNOW_FEE", + "mutant": "line 68: <= -> > (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "EMERGENCY_SURCHARGE", + "EXPRESS_SURCHARGE", + "LARGE_AREA_RATE", + "MEDIUM_AREA_RATE", + "MEDIUM_AREA_UPPER", + "TRAVEL_CHARGED", + "TRAVEL_NOT_SURCHARGED", + "UNKNOWN_PRIORITY" + ] + }, + { + "object": "{{P}}SNOW_FEE", + "mutant": "line 88: - -> + (arith)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "EMERGENCY_SURCHARGE", + "EXPRESS_SURCHARGE", + "LARGE_AREA_RATE", + "MEDIUM_AREA_RATE", + "MEDIUM_AREA_UPPER", + "MINIMUM_CHARGE", + "MINIMUM_WITH_EXPRESS", + "SMALL_AREA_RATE", + "TRAVEL_CHARGED", + "TRAVEL_NOT_SURCHARGED", + "UNKNOWN_PRIORITY" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1042/reference/snow_fee.clas.abap b/tasks_gen/train/G1042/reference/snow_fee.clas.abap index b02e96d..0072e90 100644 --- a/tasks_gen/train/G1042/reference/snow_fee.clas.abap +++ b/tasks_gen/train/G1042/reference/snow_fee.clas.abap @@ -13,14 +13,19 @@ CLASS {{p}}snow_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. PRIVATE SECTION. CONSTANTS: - c_rate_small TYPE ty_fee VALUE '0.20', - c_rate_medium TYPE ty_fee VALUE '0.16', - c_rate_large TYPE ty_fee VALUE '0.12', - c_rate_per_km TYPE ty_fee VALUE '0.40', - c_minimum_fee TYPE ty_fee VALUE '45.00', - c_free_km TYPE ty_distance VALUE 10, - c_priority_expr TYPE ty_priority VALUE 'E', - c_priority_emerg TYPE ty_priority VALUE 'X'. + c_rate_small TYPE ty_fee VALUE '0.20', + c_rate_medium TYPE ty_fee VALUE '0.16', + c_rate_large TYPE ty_fee VALUE '0.12', + c_rate_per_km TYPE ty_fee VALUE '0.40', + c_minimum_fee TYPE ty_fee VALUE '45.00', + c_free_km TYPE ty_distance VALUE 10, + c_priority_expr TYPE ty_priority VALUE 'E', + c_priority_emerg TYPE ty_priority VALUE 'X', + c_factor_express TYPE i VALUE 125, + c_factor_emergency TYPE i VALUE 150, + c_factor_standard TYPE i VALUE 100, + c_threshold_small TYPE ty_area VALUE 500, + c_threshold_medium TYPE ty_area VALUE 2000. METHODS base_fee IMPORTING iv_area TYPE ty_area @@ -49,7 +54,8 @@ CLASS {{p}}snow_fee IMPLEMENTATION. ENDMETHOD. METHOD base_fee. - DATA(lv_amount) = iv_area * rate_per_sqm( iv_area ). + DATA lv_amount TYPE ty_fee. + lv_amount = iv_area * rate_per_sqm( iv_area ). IF lv_amount < c_minimum_fee. lv_amount = c_minimum_fee. ENDIF. @@ -57,9 +63,9 @@ CLASS {{p}}snow_fee IMPLEMENTATION. ENDMETHOD. METHOD rate_per_sqm. - IF iv_area <= 500. + IF iv_area <= c_threshold_small. rv_rate = c_rate_small. - ELSEIF iv_area <= 2000. + ELSEIF iv_area <= c_threshold_medium. rv_rate = c_rate_medium. ELSE. rv_rate = c_rate_large. @@ -69,16 +75,17 @@ CLASS {{p}}snow_fee IMPLEMENTATION. METHOD surcharge_factor. CASE iv_priority. WHEN c_priority_expr. - rv_factor = 125. + rv_factor = c_factor_express. WHEN c_priority_emerg. - rv_factor = 150. + rv_factor = c_factor_emergency. WHEN OTHERS. - rv_factor = 100. + rv_factor = c_factor_standard. ENDCASE. ENDMETHOD. METHOD travel_cost. - DATA(lv_chargeable_km) = iv_distance - c_free_km. + DATA lv_chargeable_km TYPE ty_distance. + lv_chargeable_km = iv_distance - c_free_km. IF lv_chargeable_km > 0. rv_cost = lv_chargeable_km * c_rate_per_km. ENDIF. diff --git a/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap b/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap index e28a470..bf1c5f1 100644 --- a/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap +++ b/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap @@ -11,10 +11,10 @@ CLASS ltc_snow_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLE METHODS minimum_charge FOR TESTING. METHODS small_area_rate FOR TESTING. + METHODS medium_area_rate FOR TESTING. METHODS express_surcharge FOR TESTING. METHODS travel_charged FOR TESTING. METHODS zero_area_returns_zero FOR TESTING. - METHODS unknown_priority FOR TESTING. ENDCLASS. @@ -24,10 +24,8 @@ CLASS ltc_snow_fee IMPLEMENTATION. ENDMETHOD. METHOD assert_fee. - DATA lv_expected TYPE {{p}}snow_fee=>ty_fee. - lv_expected = iv_expected. cl_abap_unit_assert=>assert_equals( - exp = lv_expected + exp = iv_expected act = mo_cut->calculate( iv_area = iv_area iv_priority = iv_priority iv_distance = iv_distance ) ). @@ -41,6 +39,10 @@ CLASS ltc_snow_fee IMPLEMENTATION. assert_fee( iv_area = 500 iv_priority = 'S' iv_distance = 0 iv_expected = '100.00' ). ENDMETHOD. + METHOD medium_area_rate. + assert_fee( iv_area = 625 iv_priority = 'S' iv_distance = 0 iv_expected = '100.00' ). + ENDMETHOD. + METHOD express_surcharge. assert_fee( iv_area = 1000 iv_priority = 'E' iv_distance = 0 iv_expected = '200.00' ). ENDMETHOD. @@ -52,8 +54,4 @@ CLASS ltc_snow_fee IMPLEMENTATION. METHOD zero_area_returns_zero. assert_fee( iv_area = 0 iv_priority = 'X' iv_distance = 30 iv_expected = '0.00' ). ENDMETHOD. - - METHOD unknown_priority. - assert_fee( iv_area = 1000 iv_priority = 'Z' iv_distance = 0 iv_expected = '160.00' ). - ENDMETHOD. ENDCLASS. diff --git a/tasks_gen/train/G1042/seed/snow_fee.clas.abap b/tasks_gen/train/G1042/seed/snow_fee.clas.abap index 04ce9ea..2efed07 100644 --- a/tasks_gen/train/G1042/seed/snow_fee.clas.abap +++ b/tasks_gen/train/G1042/seed/snow_fee.clas.abap @@ -15,7 +15,6 @@ ENDCLASS. CLASS {{p}}snow_fee IMPLEMENTATION. METHOD calculate. - " legacy implementation - the result must not change DATA: lv_rate TYPE ty_fee, lv_base TYPE ty_fee, lv_sum TYPE ty_fee, diff --git a/tasks_gen/train/G1044/faulty/m2_escape_checker.clas.abap b/tasks_gen/train/G1044/faulty/m2_escape_checker.clas.abap new file mode 100644 index 0000000..788afb5 --- /dev/null +++ b/tasks_gen/train/G1044/faulty/m2_escape_checker.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}escape_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_room TYPE c LENGTH 4. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS check_booking + IMPORTING iv_room TYPE ty_room + iv_players TYPE i + iv_youngest_age TYPE i + iv_lead_hours TYPE i + RETURNING VALUE(rv_price) TYPE ty_price + RAISING {{p}}cx_escape_room + {{p}}cx_escape_players + {{p}}cx_escape_age + {{p}}cx_escape_time. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rule, + room_code TYPE ty_room, + min_players TYPE i, + max_players TYPE i, + min_age TYPE i, + price_per_player TYPE ty_price, + min_lead_hours TYPE i, + END OF ty_rule, + tt_rule TYPE HASHED TABLE OF ty_rule WITH UNIQUE KEY room_code. + + METHODS rules + RETURNING VALUE(rt_rule) TYPE tt_rule. + METHODS find_rule + IMPORTING iv_room TYPE ty_room + RETURNING VALUE(rs_rule) TYPE ty_rule + RAISING {{p}}cx_escape_room. + METHODS check_players + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RAISING {{p}}cx_escape_players. + METHODS check_age + IMPORTING is_rule TYPE ty_rule + iv_age TYPE i + RAISING {{p}}cx_escape_age. + METHODS check_lead_hours + IMPORTING is_rule TYPE ty_rule + iv_lead_hours TYPE i + RAISING {{p}}cx_escape_time. + METHODS booking_price + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}escape_checker IMPLEMENTATION. + METHOD check_booking. + DATA(ls_rule) = find_rule( iv_room ). + check_players( is_rule = ls_rule iv_players = iv_players ). + check_age( is_rule = ls_rule iv_age = iv_youngest_age ). + check_lead_hours( is_rule = ls_rule iv_lead_hours = iv_lead_hours ). + rv_price = booking_price( is_rule = ls_rule iv_players = iv_players ). + ENDMETHOD. + + METHOD rules. + rt_rule = VALUE #( + ( room_code = 'MARS' min_players = 2 max_players = 6 min_age = 12 price_per_player = 25 min_lead_hours = 2 ) + ( room_code = 'DEEP' min_players = 3 max_players = 8 min_age = 16 price_per_player = 30 min_lead_hours = 4 ) + ( room_code = 'JUNG' min_players = 2 max_players = 4 min_age = 8 price_per_player = 22 min_lead_hours = 1 ) + ( room_code = 'BANK' min_players = 4 max_players = 10 min_age = 18 price_per_player = 35 min_lead_hours = 6 ) ). + ENDMETHOD. + + METHOD find_rule. + DATA lv_room TYPE ty_room. + lv_room = to_upper( replace( val = iv_room sub = ` ` with = `` occ = 0 ) ). + DATA(lt_rule) = rules( ). + READ TABLE lt_rule INTO rs_rule WITH TABLE KEY room_code = lv_room. + IF sy-subrc <> 0. + RAISE EXCEPTION TYPE {{p}}cx_escape_room + EXPORTING iv_text = |Room { iv_room } is not known|. + ENDIF. + ENDMETHOD. + + METHOD check_players. + IF iv_players < is_rule-min_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is smaller than the minimum of the room'. + ENDIF. + IF iv_players > is_rule-max_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is larger than the maximum of the room'. + ENDIF. + ENDMETHOD. + + METHOD check_age. + IF iv_age >= is_rule-min_age. + RAISE EXCEPTION TYPE {{p}}cx_escape_age + EXPORTING iv_text = 'The youngest player is too young for the room'. + ENDIF. + ENDMETHOD. + + METHOD check_lead_hours. + IF iv_lead_hours < is_rule-min_lead_hours. + RAISE EXCEPTION TYPE {{p}}cx_escape_time + EXPORTING iv_text = 'The booking is too close to the start of the game'. + ENDIF. + ENDMETHOD. + + METHOD booking_price. + rv_price = iv_players * is_rule-price_per_player. + IF iv_players = is_rule-max_players. + rv_price = rv_price * 90 / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/faulty/m3_escape_checker.clas.abap b/tasks_gen/train/G1044/faulty/m3_escape_checker.clas.abap new file mode 100644 index 0000000..1721092 --- /dev/null +++ b/tasks_gen/train/G1044/faulty/m3_escape_checker.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}escape_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_room TYPE c LENGTH 4. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS check_booking + IMPORTING iv_room TYPE ty_room + iv_players TYPE i + iv_youngest_age TYPE i + iv_lead_hours TYPE i + RETURNING VALUE(rv_price) TYPE ty_price + RAISING {{p}}cx_escape_room + {{p}}cx_escape_players + {{p}}cx_escape_age + {{p}}cx_escape_time. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rule, + room_code TYPE ty_room, + min_players TYPE i, + max_players TYPE i, + min_age TYPE i, + price_per_player TYPE ty_price, + min_lead_hours TYPE i, + END OF ty_rule, + tt_rule TYPE HASHED TABLE OF ty_rule WITH UNIQUE KEY room_code. + + METHODS rules + RETURNING VALUE(rt_rule) TYPE tt_rule. + METHODS find_rule + IMPORTING iv_room TYPE ty_room + RETURNING VALUE(rs_rule) TYPE ty_rule + RAISING {{p}}cx_escape_room. + METHODS check_players + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RAISING {{p}}cx_escape_players. + METHODS check_age + IMPORTING is_rule TYPE ty_rule + iv_age TYPE i + RAISING {{p}}cx_escape_age. + METHODS check_lead_hours + IMPORTING is_rule TYPE ty_rule + iv_lead_hours TYPE i + RAISING {{p}}cx_escape_time. + METHODS booking_price + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}escape_checker IMPLEMENTATION. + METHOD check_booking. + DATA(ls_rule) = find_rule( iv_room ). + check_players( is_rule = ls_rule iv_players = iv_players ). + check_age( is_rule = ls_rule iv_age = iv_youngest_age ). + check_lead_hours( is_rule = ls_rule iv_lead_hours = iv_lead_hours ). + rv_price = booking_price( is_rule = ls_rule iv_players = iv_players ). + ENDMETHOD. + + METHOD rules. + rt_rule = VALUE #( + ( room_code = 'MARS' min_players = 2 max_players = 6 min_age = 12 price_per_player = 25 min_lead_hours = 2 ) + ( room_code = 'DEEP' min_players = 3 max_players = 8 min_age = 16 price_per_player = 30 min_lead_hours = 4 ) + ( room_code = 'JUNG' min_players = 2 max_players = 4 min_age = 8 price_per_player = 22 min_lead_hours = 1 ) + ( room_code = 'BANK' min_players = 4 max_players = 10 min_age = 18 price_per_player = 35 min_lead_hours = 6 ) ). + ENDMETHOD. + + METHOD find_rule. + DATA lv_room TYPE ty_room. + lv_room = to_upper( replace( val = iv_room sub = ` ` with = `` occ = 0 ) ). + DATA(lt_rule) = rules( ). + READ TABLE lt_rule INTO rs_rule WITH TABLE KEY room_code = lv_room. + IF sy-subrc <> 0. + RAISE EXCEPTION TYPE {{p}}cx_escape_room + EXPORTING iv_text = |Room { iv_room } is not known|. + ENDIF. + ENDMETHOD. + + METHOD check_players. + IF iv_players < is_rule-min_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is smaller than the minimum of the room'. + ENDIF. + IF iv_players > is_rule-max_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is larger than the maximum of the room'. + ENDIF. + ENDMETHOD. + + METHOD check_age. + IF iv_age < is_rule-min_age. + RAISE EXCEPTION TYPE {{p}}cx_escape_age + EXPORTING iv_text = 'The youngest player is too young for the room'. + ENDIF. + ENDMETHOD. + + METHOD check_lead_hours. + IF iv_lead_hours < is_rule-min_lead_hours. + RAISE EXCEPTION TYPE {{p}}cx_escape_time + EXPORTING iv_text = 'The booking is too close to the start of the game'. + ENDIF. + ENDMETHOD. + + METHOD booking_price. + rv_price = iv_players * is_rule-price_per_player. + IF iv_players <> is_rule-max_players. + rv_price = rv_price * 90 / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/faulty/m4_escape_checker.clas.abap b/tasks_gen/train/G1044/faulty/m4_escape_checker.clas.abap new file mode 100644 index 0000000..74df752 --- /dev/null +++ b/tasks_gen/train/G1044/faulty/m4_escape_checker.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}escape_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_room TYPE c LENGTH 4. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS check_booking + IMPORTING iv_room TYPE ty_room + iv_players TYPE i + iv_youngest_age TYPE i + iv_lead_hours TYPE i + RETURNING VALUE(rv_price) TYPE ty_price + RAISING {{p}}cx_escape_room + {{p}}cx_escape_players + {{p}}cx_escape_age + {{p}}cx_escape_time. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rule, + room_code TYPE ty_room, + min_players TYPE i, + max_players TYPE i, + min_age TYPE i, + price_per_player TYPE ty_price, + min_lead_hours TYPE i, + END OF ty_rule, + tt_rule TYPE HASHED TABLE OF ty_rule WITH UNIQUE KEY room_code. + + METHODS rules + RETURNING VALUE(rt_rule) TYPE tt_rule. + METHODS find_rule + IMPORTING iv_room TYPE ty_room + RETURNING VALUE(rs_rule) TYPE ty_rule + RAISING {{p}}cx_escape_room. + METHODS check_players + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RAISING {{p}}cx_escape_players. + METHODS check_age + IMPORTING is_rule TYPE ty_rule + iv_age TYPE i + RAISING {{p}}cx_escape_age. + METHODS check_lead_hours + IMPORTING is_rule TYPE ty_rule + iv_lead_hours TYPE i + RAISING {{p}}cx_escape_time. + METHODS booking_price + IMPORTING is_rule TYPE ty_rule + iv_players TYPE i + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}escape_checker IMPLEMENTATION. + METHOD check_booking. + DATA(ls_rule) = find_rule( iv_room ). + check_players( is_rule = ls_rule iv_players = iv_players ). + check_age( is_rule = ls_rule iv_age = iv_youngest_age ). + check_lead_hours( is_rule = ls_rule iv_lead_hours = iv_lead_hours ). + rv_price = booking_price( is_rule = ls_rule iv_players = iv_players ). + ENDMETHOD. + + METHOD rules. + rt_rule = VALUE #( + ( room_code = 'MARS' min_players = 2 max_players = 6 min_age = 12 price_per_player = 25 min_lead_hours = 2 ) + ( room_code = 'DEEP' min_players = 3 max_players = 8 min_age = 16 price_per_player = 30 min_lead_hours = 4 ) + ( room_code = 'JUNG' min_players = 2 max_players = 4 min_age = 8 price_per_player = 22 min_lead_hours = 1 ) + ( room_code = 'BANK' min_players = 4 max_players = 10 min_age = 18 price_per_player = 35 min_lead_hours = 6 ) ). + ENDMETHOD. + + METHOD find_rule. + DATA lv_room TYPE ty_room. + lv_room = to_upper( replace( val = iv_room sub = ` ` with = `` occ = 0 ) ). + DATA(lt_rule) = rules( ). + READ TABLE lt_rule INTO rs_rule WITH TABLE KEY room_code = lv_room. + IF sy-subrc <> 0. + RAISE EXCEPTION TYPE {{p}}cx_escape_room + EXPORTING iv_text = |Room { iv_room } is not known|. + ENDIF. + ENDMETHOD. + + METHOD check_players. + IF iv_players < is_rule-min_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is smaller than the minimum of the room'. + ENDIF. + IF iv_players > is_rule-max_players. + RAISE EXCEPTION TYPE {{p}}cx_escape_players + EXPORTING iv_text = 'The group is larger than the maximum of the room'. + ENDIF. + ENDMETHOD. + + METHOD check_age. + IF iv_age < is_rule-min_age. + RAISE EXCEPTION TYPE {{p}}cx_escape_age + EXPORTING iv_text = 'The youngest player is too young for the room'. + ENDIF. + ENDMETHOD. + + METHOD check_lead_hours. + IF iv_lead_hours < is_rule-min_lead_hours. + RAISE EXCEPTION TYPE {{p}}cx_escape_time + EXPORTING iv_text = 'The booking is too close to the start of the game'. + ENDIF. + ENDMETHOD. + + METHOD booking_price. + rv_price = iv_players * is_rule-price_per_player. + IF iv_players = is_rule-max_players. + rv_price = rv_price / 90 / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/generation.json b/tasks_gen/train/G1044/generation.json new file mode 100644 index 0000000..5a7af47 --- /dev/null +++ b/tasks_gen/train/G1044/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1044", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1044/mutation.json b/tasks_gen/train/G1044/mutation.json new file mode 100644 index 0000000..cf9036f --- /dev/null +++ b/tasks_gen/train/G1044/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1044", + "mutants": [ + { + "object": "{{P}}ESCAPE_CHECKER", + "mutant": "line 66: 16 -> 17 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "PRICE_DEEP_OK" + ] + }, + { + "object": "{{P}}ESCAPE_CHECKER", + "mutant": "line 68: 35 -> 36 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "PRICE_BANK_OK" + ] + }, + { + "object": "{{P}}ESCAPE_CHECKER", + "mutant": "line 94: < -> >= (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "LEAD_TIME_TOO_SHORT", + "LOWER_CASE_ROOM", + "PRICE_BANK_OK", + "PRICE_DEEP_OK", + "PRICE_GROUP_DISCOUNT", + "PRICE_JUNG_OK", + "PRICE_MARS_OK", + "TOO_YOUNG" + ] + }, + { + "object": "{{P}}ESCAPE_CHECKER", + "mutant": "line 109: = -> <> (eq)", + "status": "killed", + "hidden": "6/12", + "failed_tests": [ + "LOWER_CASE_ROOM", + "PRICE_BANK_OK", + "PRICE_DEEP_OK", + "PRICE_GROUP_DISCOUNT", + "PRICE_JUNG_OK", + "PRICE_MARS_OK" + ] + }, + { + "object": "{{P}}ESCAPE_CHECKER", + "mutant": "line 110: * -> / (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "PRICE_GROUP_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1045/faulty/m0_cinema_pricer.clas.abap b/tasks_gen/train/G1045/faulty/m0_cinema_pricer.clas.abap new file mode 100644 index 0000000..d5e5ec4 --- /dev/null +++ b/tasks_gen/train/G1045/faulty/m0_cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_true. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) * iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 100 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen = {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/faulty/m1_cinema_pricer.clas.abap b/tasks_gen/train/G1045/faulty/m1_cinema_pricer.clas.abap new file mode 100644 index 0000000..527c1a4 --- /dev/null +++ b/tasks_gen/train/G1045/faulty/m1_cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) / iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 100 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen = {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/faulty/m2_cinema_pricer.clas.abap b/tasks_gen/train/G1045/faulty/m2_cinema_pricer.clas.abap new file mode 100644 index 0000000..5ef7720 --- /dev/null +++ b/tasks_gen/train/G1045/faulty/m2_cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) * iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 101 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen = {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/faulty/m3_cinema_pricer.clas.abap b/tasks_gen/train/G1045/faulty/m3_cinema_pricer.clas.abap new file mode 100644 index 0000000..055539a --- /dev/null +++ b/tasks_gen/train/G1045/faulty/m3_cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) * iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 100 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen <> {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/faulty/m4_cinema_pricer.clas.abap b/tasks_gen/train/G1045/faulty/m4_cinema_pricer.clas.abap new file mode 100644 index 0000000..965de35 --- /dev/null +++ b/tasks_gen/train/G1045/faulty/m4_cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) * iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 100 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen = {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + AND iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/generation.json b/tasks_gen/train/G1045/generation.json new file mode 100644 index 0000000..7adf9a2 --- /dev/null +++ b/tasks_gen/train/G1045/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1045", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1045/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1045/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..3c3bf20 --- /dev/null +++ b/tasks_gen/train/G1045/hidden/t21_hidden.clas.abap @@ -0,0 +1,217 @@ +CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_cinema_price. + + METHODS setup. + METHODS screen_prices FOR TESTING. + METHODS visitor_discounts FOR TESTING. + METHODS late_show_surcharge FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS loyalty_discount FOR TESTING. + METHODS rounds_to_cents FOR TESTING. + METHODS invalid_screen FOR TESTING. + METHODS invalid_visitor FOR TESTING. + METHODS invalid_quantity FOR TESTING. + METHODS invalid_hour FOR TESTING. + METHODS reason_order FOR TESTING. + + METHODS result_of + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_quantity TYPE i + iv_hour TYPE i + iv_loyalty TYPE abap_bool + RETURNING VALUE(rs_result) TYPE {{p}}if_cinema_price=>ty_result. + METHODS amount + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_amount) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}cinema_pricer( ). + ENDMETHOD. + + METHOD screen_prices. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '9.50' ) + act = ls_result-price + msg = 'standard' ). + ls_result = result_of( iv_screen = 'T' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '12.00' ) + act = ls_result-price + msg = 'three-d' ). + ls_result = result_of( iv_screen = 'L' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '15.00' ) + act = ls_result-price + msg = 'large' ). + ENDMETHOD. + + METHOD visitor_discounts. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'C' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '6.65' ) + act = ls_result-price + msg = 'child' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'S' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '5.70' ) + act = ls_result-price + msg = 'senior' ). + ENDMETHOD. + + METHOD late_show_surcharge. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 19 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '9.50' ) + act = ls_result-price + msg = 'before 20' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 20 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '11.00' ) + act = ls_result-price + msg = 'from 20' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 23 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '11.00' ) + act = ls_result-price + msg = 'late' ). + ENDMETHOD. + + METHOD group_discount. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 5 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '47.50' ) + act = ls_result-price + msg = 'below limit' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 6 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '51.30' ) + act = ls_result-price + msg = 'from 6' ). + ENDMETHOD. + + METHOD loyalty_discount. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 2 iv_hour = 18 + iv_loyalty = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = amount( '18.05' ) + act = ls_result-price + msg = 'loyalty' ). + ENDMETHOD. + + METHOD rounds_to_cents. + DATA(ls_result) = result_of( iv_screen = 'T' iv_visitor = 'C' + iv_quantity = 6 iv_hour = 21 + iv_loyalty = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = amount( '50.79' ) + act = ls_result-price + msg = 'rounded' ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD invalid_screen. + DATA(ls_result) = result_of( iv_screen = 'X' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-screen + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = amount( '0.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_visitor. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'X' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-visitor + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = amount( '0.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_quantity. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 0 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-quantity + act = ls_result-reason + msg = 'zero' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 21 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-quantity + act = ls_result-reason + msg = 'too many' ). + ENDMETHOD. + + METHOD invalid_hour. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 24 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-hour + act = ls_result-reason + msg = '24' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = -1 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-hour + act = ls_result-reason + msg = 'negative' ). + ENDMETHOD. + + METHOD reason_order. + DATA(ls_result) = result_of( iv_screen = 'X' iv_visitor = 'X' + iv_quantity = 0 iv_hour = 99 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-screen + act = ls_result-reason + msg = 'screen first' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'X' + iv_quantity = 0 iv_hour = 99 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-visitor + act = ls_result-reason + msg = 'visitor second' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 0 iv_hour = 99 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-quantity + act = ls_result-reason + msg = 'quantity third' ). + ls_result = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 99 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-hour + act = ls_result-reason + msg = 'hour fourth' ). + ENDMETHOD. + + METHOD result_of. + rs_result = mo_cut->price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_quantity = iv_quantity + iv_show_hour = iv_hour + iv_loyalty = iv_loyalty ). + ENDMETHOD. + + METHOD amount. + rv_amount = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/mutation.json b/tasks_gen/train/G1045/mutation.json new file mode 100644 index 0000000..1fa2fcb --- /dev/null +++ b/tasks_gen/train/G1045/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1045", + "mutants": [ + { + "object": "{{P}}CINEMA_PRICER", + "mutant": "line 37: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "0/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "INVALID_HOUR", + "INVALID_QUANTITY", + "INVALID_SCREEN", + "INVALID_VISITOR", + "LATE_SHOW_SURCHARGE", + "LOYALTY_DISCOUNT", + "REASON_ORDER", + "ROUNDS_TO_CENTS", + "SCREEN_PRICES", + "VISITOR_DISCOUNTS" + ] + }, + { + "object": "{{P}}CINEMA_PRICER", + "mutant": "line 57: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "LOYALTY_DISCOUNT", + "ROUNDS_TO_CENTS" + ] + }, + { + "object": "{{P}}CINEMA_PRICER", + "mutant": "line 59: 100 -> 101 (const)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "ROUNDS_TO_CENTS" + ] + }, + { + "object": "{{P}}CINEMA_PRICER", + "mutant": "line 72: = -> <> (eq)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "INVALID_SCREEN", + "REASON_ORDER", + "SCREEN_PRICES" + ] + }, + { + "object": "{{P}}CINEMA_PRICER", + "mutant": "line 77: OR -> AND (logic)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "INVALID_HOUR", + "INVALID_QUANTITY", + "LATE_SHOW_SURCHARGE", + "LOYALTY_DISCOUNT", + "REASON_ORDER", + "ROUNDS_TO_CENTS", + "SCREEN_PRICES", + "VISITOR_DISCOUNTS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1045/reference/cinema_pricer.clas.abap b/tasks_gen/train/G1045/reference/cinema_pricer.clas.abap new file mode 100644 index 0000000..e8665b4 --- /dev/null +++ b/tasks_gen/train/G1045/reference/cinema_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}cinema_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_cinema_price. + + PRIVATE SECTION. + CONSTANTS: + c_late_surcharge TYPE {{p}}if_cinema_price=>ty_amount VALUE '1.50', + c_late_hour TYPE i VALUE 20, + c_group_limit TYPE i VALUE 6, + c_group_percent TYPE i VALUE 10, + c_loyalty_pct TYPE i VALUE 5, + c_max_tickets TYPE i VALUE 20, + c_max_hour TYPE i VALUE 23. + + METHODS is_known_screen + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS is_known_visitor + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_known) TYPE abap_bool. + METHODS base_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. + METHODS discount_percent + IMPORTING iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + RETURNING VALUE(rv_percent) TYPE i. + METHODS ticket_price + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_show_hour TYPE i + RETURNING VALUE(rv_price) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}cinema_pricer IMPLEMENTATION. + METHOD {{p}}if_cinema_price~price. + IF is_known_screen( iv_screen ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-screen. + RETURN. + ENDIF. + IF is_known_visitor( iv_visitor ) = abap_false. + rs_result-reason = {{p}}if_cinema_price=>c_reason-visitor. + RETURN. + ENDIF. + IF iv_quantity < 1 OR iv_quantity > c_max_tickets. + rs_result-reason = {{p}}if_cinema_price=>c_reason-quantity. + RETURN. + ENDIF. + IF iv_show_hour < 0 OR iv_show_hour > c_max_hour. + rs_result-reason = {{p}}if_cinema_price=>c_reason-hour. + RETURN. + ENDIF. + + DATA lv_total TYPE {{p}}if_cinema_price=>ty_amount. + lv_total = ticket_price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_show_hour = iv_show_hour ) * iv_quantity. + IF iv_quantity >= c_group_limit. + lv_total = lv_total * ( 100 - c_group_percent ) / 100. + ENDIF. + IF iv_loyalty = abap_true. + lv_total = lv_total * ( 100 - c_loyalty_pct ) / 100. + ENDIF. + + rs_result-price = round( val = lv_total dec = 2 ). + rs_result-reason = {{p}}if_cinema_price=>c_reason-ok. + ENDMETHOD. + + METHOD is_known_screen. + rv_known = xsdbool( iv_screen = {{p}}if_cinema_price=>c_screen-standard + OR iv_screen = {{p}}if_cinema_price=>c_screen-three_d + OR iv_screen = {{p}}if_cinema_price=>c_screen-large ). + ENDMETHOD. + + METHOD is_known_visitor. + rv_known = xsdbool( iv_visitor = {{p}}if_cinema_price=>c_visitor-adult + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-child + OR iv_visitor = {{p}}if_cinema_price=>c_visitor-senior ). + ENDMETHOD. + + METHOD base_price. + CASE iv_screen. + WHEN {{p}}if_cinema_price=>c_screen-standard. + rv_price = '9.50'. + WHEN {{p}}if_cinema_price=>c_screen-three_d. + rv_price = '12.00'. + WHEN {{p}}if_cinema_price=>c_screen-large. + rv_price = '15.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + CASE iv_visitor. + WHEN {{p}}if_cinema_price=>c_visitor-child. + rv_percent = 30. + WHEN {{p}}if_cinema_price=>c_visitor-senior. + rv_percent = 40. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + + METHOD ticket_price. + rv_price = base_price( iv_screen ). + rv_price = rv_price * ( 100 - discount_percent( iv_visitor ) ) / 100. + IF iv_show_hour >= c_late_hour. + rv_price = rv_price + c_late_surcharge. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/reference/cinema_pricer.testclasses.abap b/tasks_gen/train/G1045/reference/cinema_pricer.testclasses.abap new file mode 100644 index 0000000..0c0615f --- /dev/null +++ b/tasks_gen/train/G1045/reference/cinema_pricer.testclasses.abap @@ -0,0 +1,95 @@ +CLASS ltc_cinema_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_cinema_price. + + METHODS setup. + METHODS standard_adult FOR TESTING. + METHODS child_ticket FOR TESTING. + METHODS late_show FOR TESTING. + METHODS group_and_loyalty FOR TESTING. + METHODS invalid_screen FOR TESTING. + METHODS invalid_quantity FOR TESTING. + + METHODS result_of + IMPORTING iv_screen TYPE {{p}}if_cinema_price=>ty_screen + iv_visitor TYPE {{p}}if_cinema_price=>ty_visitor + iv_quantity TYPE i + iv_hour TYPE i + iv_loyalty TYPE abap_bool + RETURNING VALUE(rs_result) TYPE {{p}}if_cinema_price=>ty_result. + METHODS amount + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_amount) TYPE {{p}}if_cinema_price=>ty_amount. +ENDCLASS. + + +CLASS ltc_cinema_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}cinema_pricer( ). + ENDMETHOD. + + METHOD standard_adult. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '9.50' ) + act = ls_result-price ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD child_ticket. + DATA(ls_result) = result_of( iv_screen = 'T' iv_visitor = 'C' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '8.40' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD late_show. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 20 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = amount( '11.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD group_and_loyalty. + DATA(ls_result) = result_of( iv_screen = 'T' iv_visitor = 'C' + iv_quantity = 6 iv_hour = 21 + iv_loyalty = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = amount( '50.79' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_screen. + DATA(ls_result) = result_of( iv_screen = 'X' iv_visitor = 'A' + iv_quantity = 1 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-screen + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = amount( '0.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_quantity. + DATA(ls_result) = result_of( iv_screen = 'S' iv_visitor = 'A' + iv_quantity = 0 iv_hour = 18 + iv_loyalty = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_cinema_price=>c_reason-quantity + act = ls_result-reason ). + ENDMETHOD. + + METHOD result_of. + rs_result = mo_cut->price( iv_screen = iv_screen + iv_visitor = iv_visitor + iv_quantity = iv_quantity + iv_show_hour = iv_hour + iv_loyalty = iv_loyalty ). + ENDMETHOD. + + METHOD amount. + rv_amount = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1045/seed/if_cinema_price.intf.abap b/tasks_gen/train/G1045/seed/if_cinema_price.intf.abap new file mode 100644 index 0000000..d2452d4 --- /dev/null +++ b/tasks_gen/train/G1045/seed/if_cinema_price.intf.abap @@ -0,0 +1,42 @@ +INTERFACE {{p}}if_cinema_price PUBLIC. + TYPES ty_screen TYPE c LENGTH 1. + TYPES ty_visitor TYPE c LENGTH 1. + TYPES ty_reason TYPE c LENGTH 12. + TYPES ty_amount TYPE p LENGTH 7 DECIMALS 2. + + TYPES: BEGIN OF ty_result, + price TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_screen, + standard TYPE ty_screen VALUE 'S', + three_d TYPE ty_screen VALUE 'T', + large TYPE ty_screen VALUE 'L', + END OF c_screen. + + CONSTANTS: + BEGIN OF c_visitor, + adult TYPE ty_visitor VALUE 'A', + child TYPE ty_visitor VALUE 'C', + senior TYPE ty_visitor VALUE 'S', + END OF c_visitor. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + screen TYPE ty_reason VALUE 'SCREEN', + visitor TYPE ty_reason VALUE 'VISITOR', + quantity TYPE ty_reason VALUE 'QUANTITY', + hour TYPE ty_reason VALUE 'HOUR', + END OF c_reason. + + METHODS price + IMPORTING iv_screen TYPE ty_screen + iv_visitor TYPE ty_visitor + iv_quantity TYPE i + iv_show_hour TYPE i + iv_loyalty TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1045/spec.md b/tasks_gen/train/G1045/spec.md new file mode 100644 index 0000000..c2c31b5 --- /dev/null +++ b/tasks_gen/train/G1045/spec.md @@ -0,0 +1,62 @@ +# 1. Goal +A cinema multiplex prices ticket orders. The checkout program calls one class +for each order. The class calculates the total price of the order and returns a +reason code. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_CINEMA_PRICE exists in package $TMP. It is active. +- The interface contains the exact signature of the calculation, the result + structure and the reason codes. Read the interface before you start. + +# 4. Contract +- Create the class {{P}}CINEMA_PRICER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class implements {{P}}IF_CINEMA_PRICE. +- Do not add other public methods. + +# 5. Business rules +The caller gives five values: the screen category, the visitor category, the +number of tickets, the hour when the show starts, and the loyalty card flag. +The calculation returns the total price and a reason code. + +Screen categories and the base price of one ticket: +- S (standard): 9.50 +- T (three-dimensional): 12.00 +- L (large format): 15.00 + +Visitor categories and the discount on the base price: +- A (adult): 0 percent +- C (child): 30 percent +- S (senior): 40 percent + +Check the input in this sequence. Stop at the first invalid value. Return a +price of zero and the reason of this value. +1. The screen category must be S, T or L. Else return the reason SCREEN. +2. The visitor category must be A, C or S. Else return the reason VISITOR. +3. The number of tickets must be 1 to 20. Else return the reason QUANTITY. +4. The hour must be 0 to 23. Else return the reason HOUR. + +Calculate the price in this sequence: +5. Take the base price of the screen category. +6. Reduce the price of one ticket by the visitor discount. +7. If the hour is 20 or later, add a late-show surcharge of 1.50 to the price + of one ticket. +8. Multiply the price of one ticket by the number of tickets. +9. If the number of tickets is 6 or more, reduce the total by 10 percent. +10. If the loyalty card flag is true, reduce the total by 5 percent. +11. Round the total to two decimal places. +12. Return the total and the reason OK. + +# 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_CINEMA_PRICE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1045/task.json b/tasks_gen/train/G1045/task.json new file mode 100644 index 0000000..257dc48 --- /dev/null +++ b/tasks_gen/train/G1045/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1045", + "category": "F", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_CINEMA_PRICE", + "file": "seed/if_cinema_price.intf.abap", + "description": "Ticket price interface. It holds the calculation signature, the result structure and the reason codes. The spec does not show the signature. The model must read the interface." + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CINEMA_PRICER", + "implements": "{{P}}IF_CINEMA_PRICE" + } + ], + "out_of_scope": [ + "{{P}}IF_CINEMA_PRICE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "Hidden tests for the cinema ticket price" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CINEMA_PRICER", + "file": "reference/cinema_pricer.clas.abap", + "description": "Cinema ticket price calculation", + "testclasses_file": "reference/cinema_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1047/generation.json b/tasks_gen/train/G1047/generation.json new file mode 100644 index 0000000..7e4822c --- /dev/null +++ b/tasks_gen/train/G1047/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1047", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/grv_ord.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/grv_ord.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1047/reference/gravel_pricer.clas.abap b/tasks_gen/train/G1047/reference/gravel_pricer.clas.abap new file mode 100644 index 0000000..8f0891a --- /dev/null +++ b/tasks_gen/train/G1047/reference/gravel_pricer.clas.abap @@ -0,0 +1,98 @@ +CLASS {{p}}gravel_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_order_id TYPE c LENGTH 10. + TYPES ty_grade TYPE c LENGTH 1. + TYPES ty_tonnes TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 10. + TYPES: + BEGIN OF ty_result, + net_amount TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + not_found TYPE ty_reason VALUE 'NOT_FOUND', + grade TYPE ty_reason VALUE 'GRADE', + quantity TYPE ty_reason VALUE 'QUANTITY', + END OF c_reason. + + METHODS price_order + IMPORTING iv_order_id TYPE ty_order_id + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES ty_work TYPE p LENGTH 15 DECIMALS 2. + TYPES: + BEGIN OF ty_order, + order_id TYPE ty_order_id, + grade TYPE ty_grade, + tonnes TYPE ty_tonnes, + END OF ty_order. + + METHODS read_order + IMPORTING iv_order_id TYPE ty_order_id + RETURNING VALUE(rs_order) TYPE ty_order. + METHODS base_price + IMPORTING iv_grade TYPE ty_grade + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS discount_percent + IMPORTING iv_tonnes TYPE ty_tonnes + RETURNING VALUE(rv_percent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}gravel_pricer IMPLEMENTATION. + METHOD price_order. + DATA(ls_order) = read_order( iv_order_id ). + IF ls_order-order_id IS INITIAL. + rs_result-reason = c_reason-not_found. + RETURN. + ENDIF. + + DATA(lv_price) = base_price( ls_order-grade ). + IF lv_price IS INITIAL. + rs_result-reason = c_reason-grade. + RETURN. + ENDIF. + + IF ls_order-tonnes <= 0. + rs_result-reason = c_reason-quantity. + RETURN. + ENDIF. + + DATA(lv_gross) = CONV ty_work( lv_price ) * CONV ty_work( ls_order-tonnes ). + DATA(lv_factor) = CONV ty_work( 100 ) - discount_percent( ls_order-tonnes ). + DATA(lv_net) = lv_gross * lv_factor / 100. + + rs_result-net_amount = round( val = lv_net dec = 2 ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD read_order. + SELECT SINGLE order_id, grade, tonnes + FROM {{p}}grv_ord + WHERE order_id = @iv_order_id + INTO @rs_order. + ENDMETHOD. + + METHOD base_price. + CASE iv_grade. + WHEN 'F'. + rv_price = '42.00'. + WHEN 'M'. + rv_price = '36.00'. + WHEN 'C'. + rv_price = '30.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_tonnes >= 20. + rv_percent = '10.00'. + ELSEIF iv_tonnes >= 5. + rv_percent = '5.00'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1047/reference/gravel_pricer.testclasses.abap b/tasks_gen/train/G1047/reference/gravel_pricer.testclasses.abap new file mode 100644 index 0000000..be1d753 --- /dev/null +++ b/tasks_gen/train/G1047/reference/gravel_pricer.testclasses.abap @@ -0,0 +1,103 @@ +CLASS ltc_gravel_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CLASS-DATA mo_env TYPE REF TO if_osql_test_environment. + DATA mo_cut TYPE REF TO {{p}}gravel_pricer. + + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS small_order_ok FOR TESTING. + METHODS medium_order_discount FOR TESTING. + METHODS bulk_order_discount FOR TESTING. + METHODS five_tonnes_discount FOR TESTING. + METHODS unknown_order FOR TESTING. + METHODS unknown_grade FOR TESTING. + METHODS zero_quantity FOR TESTING. +ENDCLASS. + + +CLASS ltc_gravel_pricer IMPLEMENTATION. + METHOD class_setup. + mo_env = cl_osql_test_environment=>create( VALUE #( ( '{{P}}GRV_ORD' ) ) ). + DATA lt_order TYPE STANDARD TABLE OF {{p}}grv_ord WITH EMPTY KEY. + lt_order = VALUE #( + ( order_id = 'ORD0000001' grade = 'F' tonnes = 3 customer = 'BuildIt Ltd' ) + ( order_id = 'ORD0000002' grade = 'M' tonnes = 12 customer = 'Roadworks AG' ) + ( order_id = 'ORD0000003' grade = 'C' tonnes = 25 customer = 'City Council' ) + ( order_id = 'ORD0000004' grade = 'F' tonnes = 5 customer = 'Stone and Co' ) + ( order_id = 'ORD0000005' grade = 'X' tonnes = 7 customer = 'Bad Data Ltd' ) + ( order_id = 'ORD0000006' grade = 'M' tonnes = 0 customer = 'Zero Ltd' ) ). + mo_env->insert_test_data( lt_order ). + ENDMETHOD. + + METHOD class_teardown. + mo_env->destroy( ). + ENDMETHOD. + + METHOD setup. + mo_cut = NEW {{p}}gravel_pricer( ). + ENDMETHOD. + + METHOD small_order_ok. + DATA(ls_result) = mo_cut->price_order( 'ORD0000001' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}gravel_pricer=>ty_amount( '126.00' ) + act = ls_result-net_amount ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD medium_order_discount. + DATA(ls_result) = mo_cut->price_order( 'ORD0000002' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}gravel_pricer=>ty_amount( '410.40' ) + act = ls_result-net_amount ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD bulk_order_discount. + DATA(ls_result) = mo_cut->price_order( 'ORD0000003' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}gravel_pricer=>ty_amount( '675.00' ) + act = ls_result-net_amount ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD five_tonnes_discount. + DATA(ls_result) = mo_cut->price_order( 'ORD0000004' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}gravel_pricer=>ty_amount( '199.50' ) + act = ls_result-net_amount ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD unknown_order. + DATA(ls_result) = mo_cut->price_order( 'ORD9999999' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-not_found + act = ls_result-reason ). + ENDMETHOD. + + METHOD unknown_grade. + DATA(ls_result) = mo_cut->price_order( 'ORD0000005' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-grade + act = ls_result-reason ). + ENDMETHOD. + + METHOD zero_quantity. + DATA(ls_result) = mo_cut->price_order( 'ORD0000006' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}gravel_pricer=>c_reason-quantity + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1047/seed/grv_ord.tabl.abap b/tasks_gen/train/G1047/seed/grv_ord.tabl.abap new file mode 100644 index 0000000..fd960c9 --- /dev/null +++ b/tasks_gen/train/G1047/seed/grv_ord.tabl.abap @@ -0,0 +1,9 @@ +@EndUserText.label : 'Gravel orders' +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +define table {{p}}grv_ord { + key order_id : abap.char(10); + grade : abap.char(1); + tonnes : abap.int4; + customer : abap.char(30); +} diff --git a/tasks_gen/train/G1047/seed/seed_grv.clas.abap b/tasks_gen/train/G1047/seed/seed_grv.clas.abap new file mode 100644 index 0000000..f594891 --- /dev/null +++ b/tasks_gen/train/G1047/seed/seed_grv.clas.abap @@ -0,0 +1,19 @@ +CLASS {{p}}seed_grv DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_grv IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_order TYPE STANDARD TABLE OF {{p}}grv_ord WITH EMPTY KEY. + lt_order = VALUE #( + ( order_id = 'ORD0000001' grade = 'F' tonnes = 3 customer = 'BuildIt Ltd' ) + ( order_id = 'ORD0000002' grade = 'M' tonnes = 12 customer = 'Roadworks AG' ) + ( order_id = 'ORD0000003' grade = 'C' tonnes = 25 customer = 'City Council' ) + ( order_id = 'ORD0000004' grade = 'F' tonnes = 5 customer = 'Stone and Co' ) ). + DELETE FROM {{p}}grv_ord. + INSERT {{p}}grv_ord FROM TABLE @lt_order. + out->write( |{ sy-dbcnt } orders inserted| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1047/spec.md b/tasks_gen/train/G1047/spec.md new file mode 100644 index 0000000..81c61e5 --- /dev/null +++ b/tasks_gen/train/G1047/spec.md @@ -0,0 +1,63 @@ +# 1. Goal +A quarry sells gravel to building companies. A sales program calls the price +calculation for each order. Calculate the net amount of one gravel order. + +# 2. Open questions +None. + +# 3. Context +- The table {{P}}GRV_ORD exists in package $TMP. It contains the gravel orders. + The columns are: + - ORDER_ID (CHAR 10): the order number. + - GRADE (CHAR 1): the gravel grade. + - TONNES (INT4): the order quantity in whole tonnes. + - CUSTOMER (CHAR 30): the name of the customer. +- The seed class {{P}}SEED_GRV fills the table with test data. + +# 4. Contract +- Create the class {{P}}GRAVEL_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Define these public types: + - TY_ORDER_ID TYPE c LENGTH 10. + - TY_GRADE TYPE c LENGTH 1. + - TY_TONNES TYPE i. + - TY_AMOUNT TYPE p LENGTH 8 DECIMALS 2. + - TY_REASON TYPE c LENGTH 10. + - TY_RESULT: a structure with the components NET_AMOUNT (TYPE ty_amount) and + REASON (TYPE ty_reason). +- Define the public constant structure C_REASON with these components of type + TY_REASON: OK, NOT_FOUND, GRADE, QUANTITY. +- Define one public method: + PRICE_ORDER + IMPORTING iv_order_id TYPE ty_order_id + RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Return the reason of the first rule that fails. +1. Read the order with the number iv_order_id from the table {{P}}GRV_ORD. If + the table has no order with this number, return NOT_FOUND. +2. The grade must be F (fine), M (medium) or C (coarse). If the grade is not + one of these three letters, return GRADE. +3. The quantity must be greater than zero. If the quantity is zero or negative, + return QUANTITY. +4. The base price of one tonne is 42.00 EUR for grade F, 36.00 EUR for grade M + and 30.00 EUR for grade C. +5. The discount depends on the order quantity: + - An order of up to 4 tonnes gets no discount. + - An order of 6 to 19 tonnes gets a discount of 5 percent. + - An order of 20 tonnes or more gets a discount of 10 percent. +6. The net amount is the base price multiplied by the quantity, less the + discount. Round the net amount to 2 decimals. Return OK. + +# 6. Constraints +- Release target: 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 the table {{P}}GRV_ORD and the seed class + {{P}}SEED_GRV. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1047/task.json b/tasks_gen/train/G1047/task.json new file mode 100644 index 0000000..9e7e8a5 --- /dev/null +++ b/tasks_gen/train/G1047/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1047", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The business rules do not state the discount for an order of exactly 5 tonnes, so the net amount of such an order cannot be calculated.", + "keywords": [ + [ + "discount", + "tonnes" + ], + [ + "5", + "tonnes" + ], + [ + "5", + "discount" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}GRV_ORD", + "file": "seed/grv_ord.tabl.abap", + "description": "Gravel orders of the quarry" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_GRV", + "file": "seed/seed_grv.clas.abap", + "description": "Fills the gravel order table with test data", + "run": true + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}GRAVEL_PRICER" + } + ], + "out_of_scope": [ + "{{P}}GRV_ORD", + "{{P}}SEED_GRV" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}GRAVEL_PRICER", + "file": "reference/gravel_pricer.clas.abap", + "description": "Calculates the net amount of one gravel order", + "testclasses_file": "reference/gravel_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1048/faulty/m0_i_truck.ddls.asddls b/tasks_gen/train/G1048/faulty/m0_i_truck.ddls.asddls new file mode 100644 index 0000000..59e3798 --- /dev/null +++ b/tasks_gen/train/G1048/faulty/m0_i_truck.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Truck with depot' +define view entity {{P}}I_TRUCK + as select from {{p}}truck as truck + left outer join {{p}}depot as depot on depot.depot_id = truck.depot_id + association [0..1] to {{p}}depot as _Depot on truck.depot_id = _Depot.depot_id +{ + key truck.truck_id as truck_id, + truck.depot_id as depot_id, + truck.capacity_kg as capacity_kg, + truck.truck_type as truck_type, + _Depot +} +where truck.capacity_kg > 0 diff --git a/tasks_gen/train/G1048/faulty/m1_i_truck.ddls.asddls b/tasks_gen/train/G1048/faulty/m1_i_truck.ddls.asddls new file mode 100644 index 0000000..d5f2ef8 --- /dev/null +++ b/tasks_gen/train/G1048/faulty/m1_i_truck.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Truck with depot' +define view entity {{P}}I_TRUCK + as select from {{p}}truck as truck + inner join {{p}}depot as depot on depot.depot_id <> truck.depot_id + association [0..1] to {{p}}depot as _Depot on truck.depot_id = _Depot.depot_id +{ + key truck.truck_id as truck_id, + truck.depot_id as depot_id, + truck.capacity_kg as capacity_kg, + truck.truck_type as truck_type, + _Depot +} +where truck.capacity_kg > 0 diff --git a/tasks_gen/train/G1048/faulty/m2_i_truck.ddls.asddls b/tasks_gen/train/G1048/faulty/m2_i_truck.ddls.asddls new file mode 100644 index 0000000..9cdcc36 --- /dev/null +++ b/tasks_gen/train/G1048/faulty/m2_i_truck.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Truck with depot' +define view entity {{P}}I_TRUCK + as select from {{p}}truck as truck + inner join {{p}}depot as depot on depot.depot_id = truck.depot_id + association [0..1] to {{p}}depot as _Depot on truck.depot_id <> _Depot.depot_id +{ + key truck.truck_id as truck_id, + truck.depot_id as depot_id, + truck.capacity_kg as capacity_kg, + truck.truck_type as truck_type, + _Depot +} +where truck.capacity_kg > 0 diff --git a/tasks_gen/train/G1048/faulty/m3_i_truck.ddls.asddls b/tasks_gen/train/G1048/faulty/m3_i_truck.ddls.asddls new file mode 100644 index 0000000..6b74a80 --- /dev/null +++ b/tasks_gen/train/G1048/faulty/m3_i_truck.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Truck with depot' +define view entity {{P}}I_TRUCK + as select from {{p}}truck as truck + inner join {{p}}depot as depot on depot.depot_id = truck.depot_id + association [0..1] to {{p}}depot as _Depot on truck.depot_id = _Depot.depot_id +{ + key truck.truck_id as truck_id, + truck.depot_id as depot_id, + truck.capacity_kg as capacity_kg, + truck.truck_type as truck_type, + _Depot +} +where truck.capacity_kg <= 0 diff --git a/tasks_gen/train/G1048/generation.json b/tasks_gen/train/G1048/generation.json new file mode 100644 index 0000000..789ad08 --- /dev/null +++ b/tasks_gen/train/G1048/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1048", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1048/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1048/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..2e1dd5a --- /dev/null +++ b/tasks_gen/train/G1048/hidden/t21_hidden.clas.abap @@ -0,0 +1,95 @@ +CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS one_row_per_truck FOR TESTING. + METHODS depot_name_via_path FOR TESTING. + METHODS city_via_path FOR TESTING. + METHODS depot_names_of_all FOR TESTING. + METHODS zero_capacity_hidden FOR TESTING. + METHODS unknown_depot_hidden FOR TESTING. + METHODS truck_type_shown FOR TESTING. + METHODS capacity_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TRUCK' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_depot TYPE STANDARD TABLE OF {{p}}depot WITH EMPTY KEY. + DATA lt_truck TYPE STANDARD TABLE OF {{p}}truck WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_depot = VALUE #( + ( client = sy-mandt depot_id = 'D1' depot_name = 'North Depot' city = 'HAMBURG' ) + ( client = sy-mandt depot_id = 'D2' depot_name = 'South Depot' city = 'MUNICH' ) ). + lt_truck = VALUE #( + ( client = sy-mandt truck_id = 'T1' depot_id = 'D1' capacity_kg = 12000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T2' depot_id = 'D1' capacity_kg = 7500 truck_type = 'FLAT' ) + ( client = sy-mandt truck_id = 'T3' depot_id = 'D2' capacity_kg = 20000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T4' depot_id = 'D2' capacity_kg = 0 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T5' depot_id = 'D9' capacity_kg = 5000 truck_type = 'BOX' ) ). + go_env->insert_test_data( lt_depot ). + go_env->insert_test_data( lt_truck ). + ENDMETHOD. + + METHOD one_row_per_truck. + SELECT COUNT(*) FROM {{p}}i_truck INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. + + METHOD depot_name_via_path. + SELECT SINGLE t~\_depot-depot_name FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T1' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'North Depot' act = lv_name ). + ENDMETHOD. + + METHOD city_via_path. + SELECT SINGLE t~\_depot-city FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T3' INTO @DATA(lv_city). + cl_abap_unit_assert=>assert_equals( exp = 'MUNICH' act = lv_city ). + ENDMETHOD. + + METHOD depot_names_of_all. + SELECT t~truck_id, t~\_depot-depot_name AS depot_name + FROM {{p}}i_truck AS t INTO TABLE @DATA(lt_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_rows ) ). + READ TABLE lt_rows INTO DATA(ls_row) WITH KEY truck_id = 'T3'. + cl_abap_unit_assert=>assert_equals( exp = 'South Depot' act = ls_row-depot_name ). + ENDMETHOD. + + METHOD zero_capacity_hidden. + SELECT SINGLE t~truck_id FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T4' INTO @DATA(lv_id). + cl_abap_unit_assert=>assert_equals( exp = 4 act = sy-subrc ). + cl_abap_unit_assert=>assert_initial( act = lv_id ). + ENDMETHOD. + + METHOD unknown_depot_hidden. + SELECT SINGLE t~truck_id FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T5' INTO @DATA(lv_id). + cl_abap_unit_assert=>assert_equals( exp = 4 act = sy-subrc ). + cl_abap_unit_assert=>assert_initial( act = lv_id ). + ENDMETHOD. + + METHOD truck_type_shown. + SELECT SINGLE t~truck_type FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T2' INTO @DATA(lv_type). + cl_abap_unit_assert=>assert_equals( exp = 'FLAT' act = lv_type ). + ENDMETHOD. + + METHOD capacity_shown. + SELECT SINGLE t~capacity_kg FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T3' INTO @DATA(lv_capacity). + cl_abap_unit_assert=>assert_equals( exp = 20000 act = lv_capacity ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1048/mutation.json b/tasks_gen/train/G1048/mutation.json new file mode 100644 index 0000000..3ffec49 --- /dev/null +++ b/tasks_gen/train/G1048/mutation.json @@ -0,0 +1,57 @@ +{ + "task": "G1048", + "mutants": [ + { + "object": "{{P}}I_TRUCK", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "5/8", + "failed_tests": [ + "DEPOT_NAMES_OF_ALL", + "ONE_ROW_PER_TRUCK", + "UNKNOWN_DEPOT_HIDDEN" + ] + }, + { + "object": "{{P}}I_TRUCK", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "5/8", + "failed_tests": [ + "DEPOT_NAMES_OF_ALL", + "ONE_ROW_PER_TRUCK", + "UNKNOWN_DEPOT_HIDDEN" + ] + }, + { + "object": "{{P}}I_TRUCK", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "5/8", + "failed_tests": [ + "CITY_VIA_PATH", + "DEPOT_NAMES_OF_ALL", + "DEPOT_NAME_VIA_PATH" + ] + }, + { + "object": "{{P}}I_TRUCK", + "mutant": "line 14: > -> <= (rel)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "CAPACITY_SHOWN", + "CITY_VIA_PATH", + "DEPOT_NAMES_OF_ALL", + "DEPOT_NAME_VIA_PATH", + "ONE_ROW_PER_TRUCK", + "TRUCK_TYPE_SHOWN", + "ZERO_CAPACITY_HIDDEN" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1048/reference/i_truck.ddls.asddls b/tasks_gen/train/G1048/reference/i_truck.ddls.asddls new file mode 100644 index 0000000..bce3e5d --- /dev/null +++ b/tasks_gen/train/G1048/reference/i_truck.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Truck with depot' +define view entity {{P}}I_TRUCK + as select from {{p}}truck as truck + inner join {{p}}depot as depot on depot.depot_id = truck.depot_id + association [0..1] to {{p}}depot as _Depot on truck.depot_id = _Depot.depot_id +{ + key truck.truck_id as truck_id, + truck.depot_id as depot_id, + truck.capacity_kg as capacity_kg, + truck.truck_type as truck_type, + _Depot +} +where truck.capacity_kg > 0 diff --git a/tasks_gen/train/G1048/reference/t21_test.clas.abap b/tasks_gen/train/G1048/reference/t21_test.clas.abap new file mode 100644 index 0000000..ef26613 --- /dev/null +++ b/tasks_gen/train/G1048/reference/t21_test.clas.abap @@ -0,0 +1,51 @@ +CLASS {{p}}t21_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS depot_of_truck FOR TESTING. + METHODS zero_capacity_absent FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TRUCK' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_depot TYPE STANDARD TABLE OF {{p}}depot WITH EMPTY KEY. + DATA lt_truck TYPE STANDARD TABLE OF {{p}}truck WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_depot = VALUE #( + ( client = sy-mandt depot_id = 'D1' depot_name = 'North Depot' city = 'HAMBURG' ) + ( client = sy-mandt depot_id = 'D2' depot_name = 'South Depot' city = 'MUNICH' ) ). + lt_truck = VALUE #( + ( client = sy-mandt truck_id = 'T1' depot_id = 'D1' capacity_kg = 12000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T2' depot_id = 'D1' capacity_kg = 7500 truck_type = 'FLAT' ) + ( client = sy-mandt truck_id = 'T3' depot_id = 'D2' capacity_kg = 20000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T4' depot_id = 'D2' capacity_kg = 0 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T5' depot_id = 'D9' capacity_kg = 5000 truck_type = 'BOX' ) ). + go_env->insert_test_data( lt_depot ). + go_env->insert_test_data( lt_truck ). + ENDMETHOD. + + METHOD depot_of_truck. + SELECT SINGLE t~\_depot-city FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T1' INTO @DATA(lv_city). + cl_abap_unit_assert=>assert_equals( exp = 'HAMBURG' act = lv_city ). + ENDMETHOD. + + METHOD zero_capacity_absent. + SELECT SINGLE t~truck_id FROM {{p}}i_truck AS t + WHERE t~truck_id = 'T4' INTO @DATA(lv_id). + cl_abap_unit_assert=>assert_equals( exp = 4 act = sy-subrc ). + cl_abap_unit_assert=>assert_initial( act = lv_id ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1048/seed/depot.tabl.asabap b/tasks_gen/train/G1048/seed/depot.tabl.asabap new file mode 100644 index 0000000..b56bb66 --- /dev/null +++ b/tasks_gen/train/G1048/seed/depot.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Depot' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}depot { + key client : abap.clnt not null; + key depot_id : abap.char(10) not null; + depot_name : abap.char(30); + city : abap.char(20); +} diff --git a/tasks_gen/train/G1048/seed/seed_data.clas.abap b/tasks_gen/train/G1048/seed/seed_data.clas.abap new file mode 100644 index 0000000..0ec063f --- /dev/null +++ b/tasks_gen/train/G1048/seed/seed_data.clas.abap @@ -0,0 +1,25 @@ +CLASS {{p}}seed_data DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_data IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}truck. + DELETE FROM {{p}}depot. + + INSERT {{p}}depot FROM TABLE @( VALUE #( + ( client = sy-mandt depot_id = 'D1' depot_name = 'North Depot' city = 'HAMBURG' ) + ( client = sy-mandt depot_id = 'D2' depot_name = 'South Depot' city = 'MUNICH' ) ) ). + + INSERT {{p}}truck FROM TABLE @( VALUE #( + ( client = sy-mandt truck_id = 'T1' depot_id = 'D1' capacity_kg = 12000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T2' depot_id = 'D1' capacity_kg = 7500 truck_type = 'FLAT' ) + ( client = sy-mandt truck_id = 'T3' depot_id = 'D2' capacity_kg = 20000 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T4' depot_id = 'D2' capacity_kg = 0 truck_type = 'BOX' ) + ( client = sy-mandt truck_id = 'T5' depot_id = 'D9' capacity_kg = 5000 truck_type = 'BOX' ) ) ). + + out->write( 'Seed data written' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1048/seed/truck.tabl.asabap b/tasks_gen/train/G1048/seed/truck.tabl.asabap new file mode 100644 index 0000000..cd52eef --- /dev/null +++ b/tasks_gen/train/G1048/seed/truck.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Truck' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}truck { + key client : abap.clnt not null; + key truck_id : abap.char(10) not null; + depot_id : abap.char(10); + capacity_kg : abap.int4; + truck_type : abap.char(4); +} diff --git a/tasks_gen/train/G1048/spec.md b/tasks_gen/train/G1048/spec.md new file mode 100644 index 0000000..b5bf7ea --- /dev/null +++ b/tasks_gen/train/G1048/spec.md @@ -0,0 +1,53 @@ +# 1. Goal + +Show each truck of the fleet together with the depot that it belongs to. A +planning program and an OData service read the data. The join must be in the +database, in a CDS view. The consumers read the depot data with a path +expression over the association of the view. + +# 2. Open questions + +None. + +# 3. Context + +- Table {{P}}DEPOT (depot), package $TMP. Key: DEPOT_ID (CHAR 10). + Fields: DEPOT_NAME (CHAR 30), CITY (CHAR 20). +- Table {{P}}TRUCK (truck), package $TMP. Key: TRUCK_ID (CHAR 10). + Fields: DEPOT_ID (CHAR 10), CAPACITY_KG (INT4, payload in kilograms), + TRUCK_TYPE (CHAR 4). +- A truck belongs to one depot. The depot is in {{P}}DEPOT. + +# 4. Contract + +- Create the CDS view entity {{P}}I_TRUCK in package $TMP. +- Elements, with these names: TRUCK_ID (key), DEPOT_ID, CAPACITY_KG, + TRUCK_TYPE. +- The view exposes the association `_Depot` to {{P}}DEPOT. The association is + an element of the view and its name is `_Depot`. +- An ABAP SQL consumer reads the depot with the path expression + `\_Depot-DEPOT_NAME` and `\_Depot-CITY`. +- The authorization check is not required (#NOT_REQUIRED). + +# 5. Business rules + +1. The view has one row for each truck with a CAPACITY_KG greater than zero. + A truck with a capacity of zero or less is not in the view. +2. The view has one row for each truck whose depot exists in {{P}}DEPOT. + A truck with a DEPOT_ID that is not in {{P}}DEPOT is not in the view. +3. TRUCK_ID, DEPOT_ID, CAPACITY_KG and TRUCK_TYPE are the values of the + truck. +4. The association `_Depot` points from a truck to the depot with the same + DEPOT_ID. The cardinality is to-one. + +# 6. Constraints + +- Release target: ABAP Platform 2025, SAP_BASIS 816. +- Coding standards: Clean ABAP. No ATC findings of priority 1 or 2. +- Out of scope: do not change {{P}}DEPOT and {{P}}TRUCK. + +# 7. Acceptance + +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1048/task.json b/tasks_gen/train/G1048/task.json new file mode 100644 index 0000000..8471ff2 --- /dev/null +++ b/tasks_gen/train/G1048/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1048", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DEPOT", + "file": "seed/depot.tabl.asabap", + "description": "Depot" + }, + { + "type": "TABL", + "name": "{{P}}TRUCK", + "file": "seed/truck.tabl.asabap", + "description": "Truck" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DATA", + "file": "seed/seed_data.clas.abap", + "description": "Seed data for depot and truck", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_TRUCK", + "fields": [ + "TRUCK_ID", + "DEPOT_ID", + "CAPACITY_KG", + "TRUCK_TYPE" + ] + } + ], + "out_of_scope": [ + "{{P}}DEPOT", + "{{P}}TRUCK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_TRUCK", + "file": "reference/i_truck.ddls.asddls", + "description": "Truck with exposed depot association" + }, + { + "type": "CLAS", + "name": "{{P}}T21_TEST", + "file": "reference/t21_test.clas.abap", + "description": "T21 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1050/faulty/m0_tailor_pricer.clas.abap b/tasks_gen/train/G1050/faulty/m0_tailor_pricer.clas.abap new file mode 100644 index 0000000..9ff2239 --- /dev/null +++ b/tasks_gen/train/G1050/faulty/m0_tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 AND lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express = abap_true. + lv_unit = lv_unit + c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total < c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity >= c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/faulty/m1_tailor_pricer.clas.abap b/tasks_gen/train/G1050/faulty/m1_tailor_pricer.clas.abap new file mode 100644 index 0000000..7edc987 --- /dev/null +++ b/tasks_gen/train/G1050/faulty/m1_tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 OR lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express <> abap_true. + lv_unit = lv_unit + c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total < c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity >= c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/faulty/m2_tailor_pricer.clas.abap b/tasks_gen/train/G1050/faulty/m2_tailor_pricer.clas.abap new file mode 100644 index 0000000..8c0b224 --- /dev/null +++ b/tasks_gen/train/G1050/faulty/m2_tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 OR lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express = abap_true. + lv_unit = lv_unit - c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total < c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity >= c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/faulty/m3_tailor_pricer.clas.abap b/tasks_gen/train/G1050/faulty/m3_tailor_pricer.clas.abap new file mode 100644 index 0000000..5a18a85 --- /dev/null +++ b/tasks_gen/train/G1050/faulty/m3_tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 OR lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express = abap_true. + lv_unit = lv_unit + c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total >= c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity >= c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/faulty/m4_tailor_pricer.clas.abap b/tasks_gen/train/G1050/faulty/m4_tailor_pricer.clas.abap new file mode 100644 index 0000000..66df5f1 --- /dev/null +++ b/tasks_gen/train/G1050/faulty/m4_tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 OR lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express = abap_true. + lv_unit = lv_unit + c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total < c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity < c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/generation.json b/tasks_gen/train/G1050/generation.json new file mode 100644 index 0000000..29bdd89 --- /dev/null +++ b/tasks_gen/train/G1050/generation.json @@ -0,0 +1,44 @@ +{ + "id": "G1050", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tailor_pricer.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"rt_factor\"", + "reference/tailor_pricer.clas.abap line 102: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "reference/tailor_pricer.clas.abap line 103: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 3 + } + }, + { + "stage": "validate", + "oracle": 88.9, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 5615 (char 5614)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1050/hidden/tailor_pricer_hidden.clas.abap b/tasks_gen/train/G1050/hidden/tailor_pricer_hidden.clas.abap new file mode 100644 index 0000000..feb6d36 --- /dev/null +++ b/tasks_gen/train/G1050/hidden/tailor_pricer_hidden.clas.abap @@ -0,0 +1,131 @@ +CLASS {{p}}tailor_hidden DEFINITION + PUBLIC + FINAL + CREATE PUBLIC + FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}tailor_pricer. + METHODS setup. + METHODS minimum_price FOR TESTING. + METHODS ten_percent_discount FOR TESTING. + METHODS twenty_percent_discount FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS express_hits_minimum FOR TESTING. + METHODS coat_lining FOR TESTING. + METHODS unknown_service FOR TESTING. + METHODS unknown_garment FOR TESTING. + METHODS zero_quantity FOR TESTING. + METHODS negative_quantity FOR TESTING. + METHODS plain_price FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tailor_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}tailor_pricer( ). + ENDMETHOD. + + METHOD minimum_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '15.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'HEM' + iv_quantity = 1 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD ten_percent_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '32.40' ) + act = mo_cut->calculate_price( iv_garment = 'SKIRT' + iv_service = 'ZIP' + iv_quantity = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD twenty_percent_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '32.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'HEM' + iv_quantity = 5 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '57.00' ) + act = mo_cut->calculate_price( iv_garment = 'JACKET' + iv_service = 'TAKE_IN' + iv_quantity = 2 + iv_express = abap_true ) ). + ENDMETHOD. + + METHOD express_hits_minimum. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '15.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'HEM' + iv_quantity = 1 + iv_express = abap_true ) ). + ENDMETHOD. + + METHOD coat_lining. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '135.00' ) + act = mo_cut->calculate_price( iv_garment = 'COAT' + iv_service = 'LINING' + iv_quantity = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD unknown_service. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'PATCH' + iv_quantity = 2 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD unknown_garment. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'SHIRT' + iv_service = 'HEM' + iv_quantity = 2 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD zero_quantity. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'HEM' + iv_quantity = 0 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD negative_quantity. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' + iv_service = 'HEM' + iv_quantity = -2 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD plain_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '18.00' ) + act = mo_cut->calculate_price( iv_garment = 'JACKET' + iv_service = 'ZIP' + iv_quantity = 1 + iv_express = abap_false ) ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/mutation.json b/tasks_gen/train/G1050/mutation.json new file mode 100644 index 0000000..804bc79 --- /dev/null +++ b/tasks_gen/train/G1050/mutation.json @@ -0,0 +1,69 @@ +{ + "task": "G1050", + "mutants": [ + { + "object": "{{P}}TAILOR_PRICER", + "mutant": "line 60: OR -> AND (logic)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "UNKNOWN_GARMENT", + "UNKNOWN_SERVICE" + ] + }, + { + "object": "{{P}}TAILOR_PRICER", + "mutant": "line 65: = -> <> (eq)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "COAT_LINING", + "EXPRESS_FEE", + "PLAIN_PRICE", + "TEN_PERCENT_DISCOUNT", + "TWENTY_PERCENT_DISCOUNT" + ] + }, + { + "object": "{{P}}TAILOR_PRICER", + "mutant": "line 66: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "EXPRESS_FEE" + ] + }, + { + "object": "{{P}}TAILOR_PRICER", + "mutant": "line 73: < -> >= (rel)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "COAT_LINING", + "EXPRESS_FEE", + "EXPRESS_HITS_MINIMUM", + "MINIMUM_PRICE", + "PLAIN_PRICE", + "TEN_PERCENT_DISCOUNT", + "TWENTY_PERCENT_DISCOUNT" + ] + }, + { + "object": "{{P}}TAILOR_PRICER", + "mutant": "line 119: >= -> < (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "COAT_LINING", + "EXPRESS_FEE", + "PLAIN_PRICE", + "TEN_PERCENT_DISCOUNT", + "TWENTY_PERCENT_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1050/reference/tailor_pricer.clas.abap b/tasks_gen/train/G1050/reference/tailor_pricer.clas.abap new file mode 100644 index 0000000..988cb88 --- /dev/null +++ b/tasks_gen/train/G1050/reference/tailor_pricer.clas.abap @@ -0,0 +1,126 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_express_fee TYPE ty_price VALUE '6.00', + c_minimum_price TYPE ty_price VALUE '15.00', + c_discount_low TYPE ty_price VALUE '10.00', + c_discount_high TYPE ty_price VALUE '20.00', + c_qty_low TYPE i VALUE 3, + c_qty_high TYPE i VALUE 5, + c_percent_base TYPE i VALUE 100. + + METHODS service_base + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_base) TYPE ty_price. + METHODS garment_factor + IMPORTING iv_garment TYPE ty_garment + RETURNING VALUE(rv_factor) TYPE ty_price. + METHODS discount_percent + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_percent) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_unit TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_discount TYPE ty_price. + + IF iv_quantity <= 0. + RETURN. + ENDIF. + + lv_base = service_base( iv_service ). + lv_factor = garment_factor( iv_garment ). + IF lv_base <= 0 OR lv_factor <= 0. + RETURN. + ENDIF. + + lv_unit = lv_base * lv_factor. + IF iv_express = abap_true. + lv_unit = lv_unit + c_express_fee. + ENDIF. + + lv_total = lv_unit * iv_quantity. + lv_discount = discount_percent( iv_quantity ). + lv_total = lv_total - lv_total * lv_discount / c_percent_base. + + IF lv_total < c_minimum_price. + rv_price = c_minimum_price. + ELSE. + rv_price = lv_total. + ENDIF. + + ENDMETHOD. + + + METHOD service_base. + CASE iv_service. + WHEN 'HEM'. + rv_base = '8.00'. + WHEN 'TAKE_IN'. + rv_base = '15.00'. + WHEN 'ZIP'. + rv_base = '12.00'. + WHEN 'SLEEVE'. + rv_base = '10.00'. + WHEN 'LINING'. + rv_base = '25.00'. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + + METHOD garment_factor. + CASE iv_garment. + WHEN 'TROUSERS'. + rv_factor = '1.00'. + WHEN 'SKIRT'. + rv_factor = '1.00'. + WHEN 'JACKET'. + rv_factor = '1.50'. + WHEN 'DRESS'. + rv_factor = '1.50'. + WHEN 'COAT'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = 0. + ENDCASE. + ENDMETHOD. + + + METHOD discount_percent. + IF iv_quantity >= c_qty_high. + rv_percent = c_discount_high. + ELSEIF iv_quantity >= c_qty_low. + rv_percent = c_discount_low. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/reference/tailor_pricer.testclasses.abap b/tasks_gen/train/G1050/reference/tailor_pricer.testclasses.abap new file mode 100644 index 0000000..02095de --- /dev/null +++ b/tasks_gen/train/G1050/reference/tailor_pricer.testclasses.abap @@ -0,0 +1,70 @@ +CLASS ltc_tailor_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}tailor_pricer. + METHODS setup. + METHODS minimum_price FOR TESTING. + METHODS ten_percent_discount FOR TESTING. + METHODS twenty_percent_discount FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS unknown_service FOR TESTING. + METHODS unknown_garment FOR TESTING. + METHODS no_quantity FOR TESTING. +ENDCLASS. + + +CLASS ltc_tailor_pricer IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}tailor_pricer( ). + ENDMETHOD. + + METHOD minimum_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '15.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' iv_service = 'HEM' + iv_quantity = 1 iv_express = abap_false ) ). + ENDMETHOD. + + METHOD ten_percent_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '32.40' ) + act = mo_cut->calculate_price( iv_garment = 'SKIRT' iv_service = 'ZIP' + iv_quantity = 3 iv_express = abap_false ) ). + ENDMETHOD. + + METHOD twenty_percent_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '32.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' iv_service = 'HEM' + iv_quantity = 5 iv_express = abap_false ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '57.00' ) + act = mo_cut->calculate_price( iv_garment = 'JACKET' iv_service = 'TAKE_IN' + iv_quantity = 2 iv_express = abap_true ) ). + ENDMETHOD. + + METHOD unknown_service. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' iv_service = 'PATCH' + iv_quantity = 2 iv_express = abap_false ) ). + ENDMETHOD. + + METHOD unknown_garment. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'SHIRT' iv_service = 'HEM' + iv_quantity = 2 iv_express = abap_false ) ). + ENDMETHOD. + + METHOD no_quantity. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}tailor_pricer=>ty_price( '0.00' ) + act = mo_cut->calculate_price( iv_garment = 'TROUSERS' iv_service = 'HEM' + iv_quantity = 0 iv_express = abap_false ) ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/seed/tailor_pricer.clas.abap b/tasks_gen/train/G1050/seed/tailor_pricer.clas.abap new file mode 100644 index 0000000..1633221 --- /dev/null +++ b/tasks_gen/train/G1050/seed/tailor_pricer.clas.abap @@ -0,0 +1,104 @@ +CLASS {{p}}tailor_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_garment TYPE c LENGTH 10. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + +ENDCLASS. + + + +CLASS {{p}}tailor_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA lv_base TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_total TYPE ty_price. + DATA lv_disc TYPE ty_price. + + rv_price = 0. + + " a job needs at least one garment + IF iv_quantity > 0. + + " base price of the service + IF iv_service = 'HEM'. + lv_base = '8.00'. + ELSEIF iv_service = 'TAKE_IN'. + lv_base = '15.00'. + ELSEIF iv_service = 'ZIP'. + lv_base = '12.00'. + ELSEIF iv_service = 'SLEEVE'. + lv_base = '10.00'. + ELSEIF iv_service = 'LINING'. + lv_base = '25.00'. + ELSE. + lv_base = '0.00'. + ENDIF. + + " factor of the garment + IF iv_garment = 'TROUSERS'. + lv_factor = '1.00'. + ELSEIF iv_garment = 'SKIRT'. + lv_factor = '1.00'. + ELSEIF iv_garment = 'JACKET'. + lv_factor = '1.50'. + ELSEIF iv_garment = 'DRESS'. + lv_factor = '1.50'. + ELSEIF iv_garment = 'COAT'. + lv_factor = '2.00'. + ELSE. + lv_factor = '0.00'. + ENDIF. + + IF lv_base > '0.00' AND lv_factor > '0.00'. + + " price of one garment + lv_total = lv_base * lv_factor. + + " express service costs more + IF iv_express = 'X'. + lv_total = lv_total + '6.00'. + ENDIF. + + " price of all garments + lv_total = lv_total * iv_quantity. + + " discount for many garments + IF iv_quantity >= 5. + lv_disc = '20.00'. + ELSEIF iv_quantity >= 3. + lv_disc = '10.00'. + ELSE. + lv_disc = '0.00'. + ENDIF. + + lv_total = lv_total - lv_total * lv_disc / 100. + + " the shop has a minimum order value + IF lv_total < '15.00'. + lv_total = '15.00'. + ENDIF. + + rv_price = lv_total. + + ENDIF. + + ENDIF. + + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1050/spec.md b/tasks_gen/train/G1050/spec.md new file mode 100644 index 0000000..f6fadbe --- /dev/null +++ b/tasks_gen/train/G1050/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Refactor the legacy class {{P}}TAILOR_PRICER in package $TMP. The class calculates +the price of an alteration job in a tailor shop. The shop terminal calls the class +for each job. The behavior of the class must stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}TAILOR_PRICER exists in package $TMP. It is active. +- The class is public and final. The public constructor has no parameters. +- The shop terminal calls the public method CALCULATE_PRICE for each job. +- The legacy implementation is correct. It is also long, deeply nested, and full of + magic numbers. It is hard to read and hard to change. +- The customers do not accept different prices. The same input must give the same + result as before. + +# 4. Contract +- The class {{P}}TAILOR_PRICER stays in package $TMP. Do not rename it. +- The class stays public and final. The public constructor stays without parameters. +- The public types ty_garment, ty_service, and ty_price stay unchanged. +- The public method stays unchanged: + calculate_price + IMPORTING iv_garment TYPE ty_garment + iv_service TYPE ty_service + iv_quantity TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +- Do not add other public methods, public attributes, or public types. + +# 5. Business rules +The legacy implementation of {{P}}TAILOR_PRICER defines the price rules. Read the +legacy code in the seed object. Keep every rule, the sequence of the rules, and all +special cases. The behavior stays the same for every input combination. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. Methods below 40 statements. Maximum nesting depth + of 3. No magic numbers, use constants. No global variables. No comment that + restates the code. +- Out of scope: do not change the public interface. Do not change the class name. + Do not add public members. Do not change the price rules. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests as local test classes in the class. diff --git a/tasks_gen/train/G1050/task.json b/tasks_gen/train/G1050/task.json new file mode 100644 index 0000000..698e37d --- /dev/null +++ b/tasks_gen/train/G1050/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1050", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}TAILOR_PRICER", + "file": "seed/tailor_pricer.clas.abap", + "description": "Legacy tailor alteration price calculation" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TAILOR_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TAILOR_HIDDEN", + "file": "hidden/tailor_pricer_hidden.clas.abap", + "description": "Hidden tests for the refactored tailor pricer" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TAILOR_PRICER", + "file": "reference/tailor_pricer.clas.abap", + "description": "Refactored tailor pricer", + "testclasses_file": "reference/tailor_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1051/faulty/m1_i_melt_out.ddls.asddls b/tasks_gen/train/G1051/faulty/m1_i_melt_out.ddls.asddls new file mode 100644 index 0000000..1b0165b --- /dev/null +++ b/tasks_gen/train/G1051/faulty/m1_i_melt_out.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head + on part.furnace_id <> head.furnace_id + and part.melt_date = head.melt_date + and part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + sum( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/faulty/m2_i_melt_out.ddls.asddls b/tasks_gen/train/G1051/faulty/m2_i_melt_out.ddls.asddls new file mode 100644 index 0000000..7f03a95 --- /dev/null +++ b/tasks_gen/train/G1051/faulty/m2_i_melt_out.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head + on part.furnace_id = head.furnace_id + or part.melt_date = head.melt_date + and part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + sum( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/faulty/m3_i_melt_out.ddls.asddls b/tasks_gen/train/G1051/faulty/m3_i_melt_out.ddls.asddls new file mode 100644 index 0000000..3f3bc49 --- /dev/null +++ b/tasks_gen/train/G1051/faulty/m3_i_melt_out.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head + on part.furnace_id = head.furnace_id + and part.melt_date <> head.melt_date + and part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + sum( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/faulty/m4_i_melt_out.ddls.asddls b/tasks_gen/train/G1051/faulty/m4_i_melt_out.ddls.asddls new file mode 100644 index 0000000..25f2db4 --- /dev/null +++ b/tasks_gen/train/G1051/faulty/m4_i_melt_out.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head + on part.furnace_id = head.furnace_id + and part.melt_date = head.melt_date + and part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + max( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/generation.json b/tasks_gen/train/G1051/generation.json new file mode 100644 index 0000000..a5b2871 --- /dev/null +++ b/tasks_gen/train/G1051/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1051", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T15 (similarity spec 0.84, rules 0.79, names 0.00). Choose a different business topic and different object names.", + "Too close to task T15 (similarity spec 0.84, rules 0.79, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1051/hidden/melt_hidden.clas.abap b/tasks_gen/train/G1051/hidden/melt_hidden.clas.abap new file mode 100644 index 0000000..02ae481 --- /dev/null +++ b/tasks_gen/train/G1051/hidden/melt_hidden.clas.abap @@ -0,0 +1,115 @@ +CLASS {{p}}melt_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS melt_f1_1010 FOR TESTING. + METHODS melt_f2_1010 FOR TESTING. + METHODS melt_f1_1011 FOR TESTING. + METHODS melt_f2_0002 FOR TESTING. + METHODS melt_f2_0009 FOR TESTING. + METHODS empty_melt_absent FOR TESTING. + METHODS row_count FOR TESTING. + METHODS alloy_kept FOR TESTING. +ENDCLASS. + + +CLASS {{p}}melt_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MELT_OUT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_melt TYPE STANDARD TABLE OF {{p}}melt WITH EMPTY KEY. + DATA lt_cast TYPE STANDARD TABLE OF {{p}}casting WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_melt = VALUE #( + ( furnace_id = 'F1' melt_date = '20240110' melt_no = '0001' alloy = 'GJL' crew = 'A' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0001' alloy = 'GJS' crew = 'B' ) + ( furnace_id = 'F1' melt_date = '20240111' melt_no = '0001' alloy = 'GJL' crew = 'A' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0002' alloy = 'GJS' crew = 'B' ) + ( furnace_id = 'F1' melt_date = '20240112' melt_no = '0009' alloy = 'GJL' crew = 'C' ) + ( furnace_id = 'F2' melt_date = '20240115' melt_no = '0009' alloy = 'GJS' crew = 'D' ) ). + lt_cast = VALUE #( + ( furnace_id = 'F1' melt_date = '20240110' melt_no = '0001' part_no = 1 part_name = 'WHEEL' weight = '12.50' ) + ( furnace_id = 'F1' melt_date = '20240110' melt_no = '0001' part_no = 2 part_name = 'HUB' weight = '7.50' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0001' part_no = 1 part_name = 'GEAR' weight = '20.00' ) + ( furnace_id = 'F1' melt_date = '20240111' melt_no = '0001' part_no = 1 part_name = 'WHEEL' weight = '12.50' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0002' part_no = 1 part_name = 'GEAR' weight = '20.00' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0002' part_no = 2 part_name = 'HUB' weight = '7.45' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0002' part_no = 3 part_name = 'SHAFT' weight = '5.05' ) + ( furnace_id = 'F2' melt_date = '20240115' melt_no = '0009' part_no = 1 part_name = 'GEAR' weight = '20.00' ) ). + go_env->insert_test_data( lt_melt ). + go_env->insert_test_data( lt_cast ). + ENDMETHOD. + + METHOD melt_f1_1010. + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F1' AND melt_date = '20240110' AND melt_no = '0001' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD melt_f2_1010. + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F2' AND melt_date = '20240110' AND melt_no = '0001' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD melt_f1_1011. + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F1' AND melt_date = '20240111' AND melt_no = '0001' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.50' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD melt_f2_0002. + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F2' AND melt_date = '20240110' AND melt_no = '0002' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '32.50' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD melt_f2_0009. + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F2' AND melt_date = '20240115' AND melt_no = '0009' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD empty_melt_absent. + SELECT COUNT(*) FROM {{p}}i_melt_out + WHERE furnace_id = 'F1' AND melt_date = '20240112' AND melt_no = '0009' + INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_melt_out INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lv_rows ). + ENDMETHOD. + + METHOD alloy_kept. + SELECT SINGLE alloy FROM {{p}}i_melt_out + WHERE furnace_id = 'F2' AND melt_date = '20240110' AND melt_no = '0001' + INTO @DATA(lv_alloy). + cl_abap_unit_assert=>assert_equals( exp = 'GJS' act = lv_alloy ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1051/mutation.json b/tasks_gen/train/G1051/mutation.json new file mode 100644 index 0000000..9058619 --- /dev/null +++ b/tasks_gen/train/G1051/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1051", + "mutants": [ + { + "object": "{{P}}I_MELT_OUT", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "8/8", + "failed_tests": [] + }, + { + "object": "{{P}}I_MELT_OUT", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "MELT_F1_1010", + "MELT_F1_1011", + "MELT_F2_0002", + "MELT_F2_0009", + "MELT_F2_1010", + "ROW_COUNT" + ] + }, + { + "object": "{{P}}I_MELT_OUT", + "mutant": "line 7: and -> or (logic)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "EMPTY_MELT_ABSENT", + "MELT_F1_1010", + "MELT_F1_1011", + "MELT_F2_0002", + "MELT_F2_0009", + "MELT_F2_1010", + "ROW_COUNT" + ] + }, + { + "object": "{{P}}I_MELT_OUT", + "mutant": "line 7: = -> <> (eq)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "ALLOY_KEPT", + "MELT_F1_1010", + "MELT_F1_1011", + "MELT_F2_0002", + "MELT_F2_0009", + "MELT_F2_1010", + "ROW_COUNT" + ] + }, + { + "object": "{{P}}I_MELT_OUT", + "mutant": "line 15: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "MELT_F1_1010", + "MELT_F2_0002" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1051/reference/i_melt_out.ddls.asddls b/tasks_gen/train/G1051/reference/i_melt_out.ddls.asddls new file mode 100644 index 0000000..eb0503d --- /dev/null +++ b/tasks_gen/train/G1051/reference/i_melt_out.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head + on part.furnace_id = head.furnace_id + and part.melt_date = head.melt_date + and part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + sum( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/reference/melt_test.clas.abap b/tasks_gen/train/G1051/reference/melt_test.clas.abap new file mode 100644 index 0000000..0a9e3b8 --- /dev/null +++ b/tasks_gen/train/G1051/reference/melt_test.clas.abap @@ -0,0 +1,53 @@ +CLASS {{p}}melt_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS same_no_other_furnace FOR TESTING. + METHODS melt_without_castings FOR TESTING. +ENDCLASS. + + +CLASS {{p}}melt_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MELT_OUT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD same_no_other_furnace. + DATA lt_melt TYPE STANDARD TABLE OF {{p}}melt WITH EMPTY KEY. + DATA lt_cast TYPE STANDARD TABLE OF {{p}}casting WITH EMPTY KEY. + lt_melt = VALUE #( + ( furnace_id = 'F1' melt_date = '20240110' melt_no = '0001' alloy = 'GJL' crew = 'A' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0001' alloy = 'GJS' crew = 'B' ) ). + lt_cast = VALUE #( + ( furnace_id = 'F1' melt_date = '20240110' melt_no = '0001' part_no = 1 part_name = 'WHEEL' weight = '12.50' ) + ( furnace_id = 'F2' melt_date = '20240110' melt_no = '0001' part_no = 1 part_name = 'GEAR' weight = '20.00' ) ). + go_env->insert_test_data( lt_melt ). + go_env->insert_test_data( lt_cast ). + SELECT SINGLE pieces, net_weight FROM {{p}}i_melt_out + WHERE furnace_id = 'F1' AND melt_date = '20240110' AND melt_no = '0001' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-pieces ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.50' ) + act = CONV decfloat34( ls_row-net_weight ) ). + ENDMETHOD. + + METHOD melt_without_castings. + DATA lt_melt TYPE STANDARD TABLE OF {{p}}melt WITH EMPTY KEY. + lt_melt = VALUE #( + ( furnace_id = 'F1' melt_date = '20240112' melt_no = '0009' alloy = 'GJL' crew = 'C' ) ). + go_env->insert_test_data( lt_melt ). + SELECT COUNT(*) FROM {{p}}i_melt_out INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1051/seed/casting.tabl.asabap b/tasks_gen/train/G1051/seed/casting.tabl.asabap new file mode 100644 index 0000000..324976c --- /dev/null +++ b/tasks_gen/train/G1051/seed/casting.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Foundry casting' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}casting { + key client : abap.clnt not null; + key furnace_id : abap.char(4) not null; + key melt_date : abap.dats not null; + key melt_no : abap.numc(4) not null; + key part_no : abap.int4 not null; + part_name : abap.char(10); + weight : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1051/seed/i_melt_out.ddls.asddls b/tasks_gen/train/G1051/seed/i_melt_out.ddls.asddls new file mode 100644 index 0000000..0a25dce --- /dev/null +++ b/tasks_gen/train/G1051/seed/i_melt_out.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Melt output' +define view entity {{P}}I_MELT_OUT + as select from {{p}}casting as part + inner join {{p}}melt as head on part.melt_no = head.melt_no +{ + key head.furnace_id as furnace_id, + key head.melt_date as melt_date, + key head.melt_no as melt_no, + head.alloy as alloy, + count( * ) as pieces, + sum( part.weight ) as net_weight +} +group by + head.furnace_id, + head.melt_date, + head.melt_no, + head.alloy diff --git a/tasks_gen/train/G1051/seed/melt.tabl.asabap b/tasks_gen/train/G1051/seed/melt.tabl.asabap new file mode 100644 index 0000000..7ed27c4 --- /dev/null +++ b/tasks_gen/train/G1051/seed/melt.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Foundry melt' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}melt { + key client : abap.clnt not null; + key furnace_id : abap.char(4) not null; + key melt_date : abap.dats not null; + key melt_no : abap.numc(4) not null; + alloy : abap.char(10); + crew : abap.char(10); +} diff --git a/tasks_gen/train/G1051/spec.md b/tasks_gen/train/G1051/spec.md new file mode 100644 index 0000000..51842ab --- /dev/null +++ b/tasks_gen/train/G1051/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +Fix the CDS view entity {{P}}I_MELT_OUT in package $TMP. The view reports the +output of the casting melts of a foundry. The production team says that the +figures in the view are wrong for many melts. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}MELT (melt header), package $TMP. Key: FURNACE_ID (CHAR 4), + MELT_DATE (DATS), MELT_NO (NUMC 4). Fields: ALLOY (CHAR 10), CREW (CHAR 10). +- Table {{P}}CASTING (casting), package $TMP. Key: FURNACE_ID (CHAR 4), + MELT_DATE (DATS), MELT_NO (NUMC 4), PART_NO (INT4). Fields: PART_NAME + (CHAR 10), WEIGHT (DEC 15,2, the weight of one casting in kilograms). +- A melt number is not unique in the foundry. The same melt number occurs in + other furnaces and on other dates. +- The CDS view entity {{P}}I_MELT_OUT already exists in package $TMP. + +The production team reports these symptoms: +- The pieces and the net weight of a melt are too high when the melt number of + the melt also occurs in another furnace or on another date. +- The view shows melts that have no castings. + +# 4. Contract +- Fix the CDS view entity {{P}}I_MELT_OUT in package $TMP. The name stays the + same. +- Elements, with these names: FURNACE_ID (key), MELT_DATE (key), MELT_NO (key), + ALLOY, PIECES, NET_WEIGHT. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each melt that has castings. A melt without + castings must not appear in the view. +2. PIECES is the number of castings of the melt. +3. NET_WEIGHT is the sum of the weights of the castings of the melt, in + kilograms with 2 decimals. +4. A casting belongs to the melt of its own furnace, its own date and its own + melt number. Castings of another furnace, of another date or of another melt + number must not contribute to the figures of a melt. +5. All other behavior of the view stays the same. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standard: Clean ABAP. +- Out of scope: do not change {{P}}MELT and {{P}}CASTING. Do not change the + element names or the data types of the view. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1051/task.json b/tasks_gen/train/G1051/task.json new file mode 100644 index 0000000..26e8188 --- /dev/null +++ b/tasks_gen/train/G1051/task.json @@ -0,0 +1,73 @@ +{ + "id": "G1051", + "category": "I", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}MELT", + "file": "seed/melt.tabl.asabap", + "description": "Foundry melt header" + }, + { + "type": "TABL", + "name": "{{P}}CASTING", + "file": "seed/casting.tabl.asabap", + "description": "Foundry casting" + }, + { + "type": "DDLS", + "name": "{{P}}I_MELT_OUT", + "file": "seed/i_melt_out.ddls.asddls", + "description": "Melt output view (faulty)" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_MELT_OUT", + "fields": [ + "FURNACE_ID", + "MELT_DATE", + "MELT_NO", + "ALLOY", + "PIECES", + "NET_WEIGHT" + ] + } + ], + "out_of_scope": [ + "{{P}}MELT", + "{{P}}CASTING" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}MELT_HIDDEN", + "file": "hidden/melt_hidden.clas.abap", + "description": "Hidden tests for the melt output view" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_MELT_OUT", + "file": "reference/i_melt_out.ddls.asddls", + "description": "Fixed melt output view" + }, + { + "type": "CLAS", + "name": "{{P}}MELT_TEST", + "file": "reference/melt_test.clas.abap", + "description": "Own tests for the melt output view" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1053/faulty/m0_rack_billing.clas.abap b/tasks_gen/train/G1053/faulty/m0_rack_billing.clas.abap new file mode 100644 index 0000000..2551e4a --- /dev/null +++ b/tasks_gen/train/G1053/faulty/m0_rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee - lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee * ( 100 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count >= 20. + rv_percent = 20. + ELSEIF iv_count >= 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/faulty/m1_rack_billing.clas.abap b/tasks_gen/train/G1053/faulty/m1_rack_billing.clas.abap new file mode 100644 index 0000000..1b83f5a --- /dev/null +++ b/tasks_gen/train/G1053/faulty/m1_rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee + lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee / ( 100 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count >= 20. + rv_percent = 20. + ELSEIF iv_count >= 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/faulty/m2_rack_billing.clas.abap b/tasks_gen/train/G1053/faulty/m2_rack_billing.clas.abap new file mode 100644 index 0000000..c066072 --- /dev/null +++ b/tasks_gen/train/G1053/faulty/m2_rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee + lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee * ( 101 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count >= 20. + rv_percent = 20. + ELSEIF iv_count >= 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/faulty/m3_rack_billing.clas.abap b/tasks_gen/train/G1053/faulty/m3_rack_billing.clas.abap new file mode 100644 index 0000000..a8f9d7e --- /dev/null +++ b/tasks_gen/train/G1053/faulty/m3_rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee + lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee * ( 100 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count < 20. + rv_percent = 20. + ELSEIF iv_count >= 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/faulty/m4_rack_billing.clas.abap b/tasks_gen/train/G1053/faulty/m4_rack_billing.clas.abap new file mode 100644 index 0000000..622780c --- /dev/null +++ b/tasks_gen/train/G1053/faulty/m4_rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee + lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee * ( 100 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count >= 20. + rv_percent = 20. + ELSEIF iv_count < 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/generation.json b/tasks_gen/train/G1053/generation.json new file mode 100644 index 0000000..a264650 --- /dev/null +++ b/tasks_gen/train/G1053/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1053", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t02_hidden.clas.abap line 7: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/rack_billing.testclasses.abap line 6: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1053/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1053/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..b021dd4 --- /dev/null +++ b/tasks_gen/train/G1053/hidden/t02_hidden.clas.abap @@ -0,0 +1,157 @@ +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}}rack_billing. + + METHODS setup. + METHODS assert_amount + IMPORTING iv_expected TYPE {{p}}rack_billing=>ty_fee + iv_actual TYPE {{p}}rack_billing=>ty_fee. + + METHODS one_rack FOR TESTING. + METHODS sums_per_customer FOR TESTING. + METHODS sorted_by_customer FOR TESTING. + METHODS ten_racks_discount FOR TESTING. + METHODS twenty_racks_discount FOR TESTING. + METHODS nine_racks_no_discount FOR TESTING. + METHODS ignores_unknown_size FOR TESTING. + METHODS ignores_unknown_power FOR TESTING. + METHODS ignores_second_rack_id FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS all_classes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}rack_billing( ). + ENDMETHOD. + + METHOD assert_amount. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD one_rack. + DATA(lt_bills) = mo_cut->calculate( VALUE #( ( rack_id = 'R001' customer = 'ALPHA' + size_class = 'M' power_class = 'B' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_bills[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '150.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD sums_per_customer. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'S' power_class = 'A' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'S' power_class = 'B' ) + ( rack_id = 'R003' customer = 'ALPHA' size_class = 'M' power_class = 'A' ) + ( rack_id = 'R004' customer = 'BETA' size_class = 'L' power_class = 'C' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_bills[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '250.00' iv_actual = lt_bills[ 1 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_bills[ 2 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 2 ]-rack_count ). + assert_amount( iv_expected = '320.00' iv_actual = lt_bills[ 2 ]-total_fee ). + ENDMETHOD. + + METHOD sorted_by_customer. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ZULU' size_class = 'S' power_class = 'A' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'S' power_class = 'A' ) + ( rack_id = 'R003' customer = 'MIKE' size_class = 'S' power_class = 'A' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_bills[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 'MIKE' act = lt_bills[ 2 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 'ZULU' act = lt_bills[ 3 ]-customer ). + ENDMETHOD. + + METHOD ten_racks_discount. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DO 10 TIMES. + INSERT VALUE #( rack_id = |R{ sy-index }| customer = 'ALPHA' + size_class = 'S' power_class = 'A' ) INTO TABLE lt_racks. + ENDDO. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '450.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD twenty_racks_discount. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DO 20 TIMES. + INSERT VALUE #( rack_id = |R{ sy-index }| customer = 'ALPHA' + size_class = 'S' power_class = 'A' ) INTO TABLE lt_racks. + ENDDO. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '800.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD nine_racks_no_discount. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DO 9 TIMES. + INSERT VALUE #( rack_id = |R{ sy-index }| customer = 'ALPHA' + size_class = 'S' power_class = 'A' ) INTO TABLE lt_racks. + ENDDO. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 9 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '450.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_unknown_size. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'X' power_class = 'A' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'S' power_class = 'A' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '50.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_unknown_power. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'L' power_class = 'Z' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'L' power_class = 'B' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '280.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_second_rack_id. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'M' power_class = 'A' ) + ( rack_id = 'R001' customer = 'BETA' size_class = 'L' power_class = 'C' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_bills[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '120.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_initial( lt_bills ). + ENDMETHOD. + + METHOD all_classes. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'S' power_class = 'C' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'M' power_class = 'C' ) + ( rack_id = 'R003' customer = 'ALPHA' size_class = 'L' power_class = 'C' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '630.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/mutation.json b/tasks_gen/train/G1053/mutation.json new file mode 100644 index 0000000..2a97847 --- /dev/null +++ b/tasks_gen/train/G1053/mutation.json @@ -0,0 +1,93 @@ +{ + "task": "G1053", + "mutants": [ + { + "object": "{{P}}RACK_BILLING", + "mutant": "line 74: + -> - (arith)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "ALL_CLASSES", + "IGNORES_SECOND_RACK_ID", + "IGNORES_UNKNOWN_POWER", + "IGNORES_UNKNOWN_SIZE", + "NINE_RACKS_NO_DISCOUNT", + "ONE_RACK", + "SUMS_PER_CUSTOMER", + "TEN_RACKS_DISCOUNT", + "TWENTY_RACKS_DISCOUNT" + ] + }, + { + "object": "{{P}}RACK_BILLING", + "mutant": "line 79: * -> / (arith)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "ALL_CLASSES", + "IGNORES_SECOND_RACK_ID", + "IGNORES_UNKNOWN_POWER", + "IGNORES_UNKNOWN_SIZE", + "NINE_RACKS_NO_DISCOUNT", + "ONE_RACK", + "SUMS_PER_CUSTOMER", + "TEN_RACKS_DISCOUNT", + "TWENTY_RACKS_DISCOUNT" + ] + }, + { + "object": "{{P}}RACK_BILLING", + "mutant": "line 79: 100 -> 101 (const)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "ALL_CLASSES", + "IGNORES_SECOND_RACK_ID", + "IGNORES_UNKNOWN_POWER", + "IGNORES_UNKNOWN_SIZE", + "NINE_RACKS_NO_DISCOUNT", + "ONE_RACK", + "SUMS_PER_CUSTOMER", + "TEN_RACKS_DISCOUNT", + "TWENTY_RACKS_DISCOUNT" + ] + }, + { + "object": "{{P}}RACK_BILLING", + "mutant": "line 110: >= -> < (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "ALL_CLASSES", + "IGNORES_SECOND_RACK_ID", + "IGNORES_UNKNOWN_POWER", + "IGNORES_UNKNOWN_SIZE", + "NINE_RACKS_NO_DISCOUNT", + "ONE_RACK", + "SUMS_PER_CUSTOMER", + "TEN_RACKS_DISCOUNT", + "TWENTY_RACKS_DISCOUNT" + ] + }, + { + "object": "{{P}}RACK_BILLING", + "mutant": "line 112: >= -> < (rel)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "ALL_CLASSES", + "IGNORES_SECOND_RACK_ID", + "IGNORES_UNKNOWN_POWER", + "IGNORES_UNKNOWN_SIZE", + "NINE_RACKS_NO_DISCOUNT", + "ONE_RACK", + "SUMS_PER_CUSTOMER", + "TEN_RACKS_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1053/reference/rack_billing.clas.abap b/tasks_gen/train/G1053/reference/rack_billing.clas.abap new file mode 100644 index 0000000..94faedc --- /dev/null +++ b/tasks_gen/train/G1053/reference/rack_billing.clas.abap @@ -0,0 +1,116 @@ +CLASS {{p}}rack_billing DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. + + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_price, + size_class TYPE ty_size, + power_class TYPE ty_power, + fee TYPE ty_fee, + END OF ty_price. + TYPES tt_prices TYPE HASHED TABLE OF ty_price WITH UNIQUE KEY size_class power_class. + TYPES tt_seen TYPE HASHED TABLE OF ty_rack WITH UNIQUE KEY rack_id. + TYPES tt_bill_hash TYPE HASHED TABLE OF ty_bill WITH UNIQUE KEY customer. + + METHODS price_list + RETURNING VALUE(rt_prices) TYPE tt_prices. + METHODS rack_fee + IMPORTING is_rack TYPE ty_rack + it_prices TYPE tt_prices + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS discount_percent + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}rack_billing IMPLEMENTATION. + METHOD calculate. + DATA(lt_prices) = price_list( ). + DATA lt_seen TYPE tt_seen. + DATA lt_bills TYPE tt_bill_hash. + + LOOP AT it_racks INTO DATA(ls_rack). + INSERT ls_rack INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + + DATA(lv_fee) = rack_fee( is_rack = ls_rack it_prices = lt_prices ). + IF lv_fee IS INITIAL. + CONTINUE. + ENDIF. + + ASSIGN lt_bills[ customer = ls_rack-customer ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( customer = ls_rack-customer ) INTO TABLE lt_bills ASSIGNING . + ENDIF. + -rack_count = -rack_count + 1. + -total_fee = -total_fee + lv_fee. + ENDLOOP. + + LOOP AT lt_bills ASSIGNING . + DATA(lv_percent) = discount_percent( -rack_count ). + -total_fee = -total_fee * ( 100 - lv_percent ) / 100. + INSERT VALUE #( customer = -customer + rack_count = -rack_count + total_fee = -total_fee ) INTO TABLE rt_bills. + ENDLOOP. + + SORT rt_bills BY customer. + ENDMETHOD. + + METHOD price_list. + rt_prices = VALUE #( + ( size_class = 'S' power_class = 'A' fee = '50.00' ) + ( size_class = 'S' power_class = 'B' fee = '80.00' ) + ( size_class = 'S' power_class = 'C' fee = '120.00' ) + ( size_class = 'M' power_class = 'A' fee = '120.00' ) + ( size_class = 'M' power_class = 'B' fee = '150.00' ) + ( size_class = 'M' power_class = 'C' fee = '190.00' ) + ( size_class = 'L' power_class = 'A' fee = '250.00' ) + ( size_class = 'L' power_class = 'B' fee = '280.00' ) + ( size_class = 'L' power_class = 'C' fee = '320.00' ) ). + ENDMETHOD. + + METHOD rack_fee. + ASSIGN it_prices[ size_class = is_rack-size_class power_class = is_rack-power_class ] + TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_fee = -fee. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF iv_count >= 20. + rv_percent = 20. + ELSEIF iv_count >= 10. + rv_percent = 10. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/reference/rack_billing.testclasses.abap b/tasks_gen/train/G1053/reference/rack_billing.testclasses.abap new file mode 100644 index 0000000..09b4486 --- /dev/null +++ b/tasks_gen/train/G1053/reference/rack_billing.testclasses.abap @@ -0,0 +1,88 @@ +CLASS ltc_rack_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}rack_billing. + + METHODS setup. + METHODS assert_amount + IMPORTING iv_expected TYPE {{p}}rack_billing=>ty_fee + iv_actual TYPE {{p}}rack_billing=>ty_fee. + + METHODS one_rack FOR TESTING. + METHODS groups_by_customer FOR TESTING. + METHODS applies_discount FOR TESTING. + METHODS ignores_invalid_rack FOR TESTING. + METHODS ignores_duplicate_rack FOR TESTING. + METHODS empty_input FOR TESTING. +ENDCLASS. + + +CLASS ltc_rack_billing IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}rack_billing( ). + ENDMETHOD. + + METHOD assert_amount. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD one_rack. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( ( rack_id = 'R001' customer = 'ALPHA' + size_class = 'M' power_class = 'B' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '150.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD groups_by_customer. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'S' power_class = 'C' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'L' power_class = 'A' ) + ( rack_id = 'R003' customer = 'BETA' size_class = 'M' power_class = 'B' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_bills ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_bills[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '370.00' iv_actual = lt_bills[ 1 ]-total_fee ). + assert_amount( iv_expected = '150.00' iv_actual = lt_bills[ 2 ]-total_fee ). + ENDMETHOD. + + METHOD applies_discount. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DO 10 TIMES. + INSERT VALUE #( rack_id = |R{ sy-index }| customer = 'ALPHA' + size_class = 'M' power_class = 'B' ) INTO TABLE lt_racks. + ENDDO. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '1350.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_invalid_rack. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'S' power_class = 'D' ) + ( rack_id = 'R002' customer = 'ALPHA' size_class = 'M' power_class = 'A' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '120.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_duplicate_rack. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + lt_racks = VALUE #( + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'S' power_class = 'A' ) + ( rack_id = 'R001' customer = 'ALPHA' size_class = 'L' power_class = 'C' ) ). + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_bills[ 1 ]-rack_count ). + assert_amount( iv_expected = '50.00' iv_actual = lt_bills[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_racks TYPE {{p}}rack_billing=>tt_racks. + DATA(lt_bills) = mo_cut->calculate( lt_racks ). + cl_abap_unit_assert=>assert_initial( lt_bills ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1053/spec.md b/tasks_gen/train/G1053/spec.md new file mode 100644 index 0000000..482b518 --- /dev/null +++ b/tasks_gen/train/G1053/spec.md @@ -0,0 +1,76 @@ +# 1. Goal +A data centre operator bills its customers for the server racks that they rent. +A billing program calls one class for each billing run. The class returns one +row per customer with the number of racks and the monthly fee. + +# 2. Open questions +None. + +# 3. Context +- There are no seed objects. Create the class in package $TMP. +- A rack has a rack ID, a customer, a size class and a power class. +- The billing input can contain the same rack more than once. +- The class is the only object of this task. + +# 4. Contract +Create the class {{P}}RACK_BILLING in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- Public types: + TYPES ty_size TYPE c LENGTH 1. + TYPES ty_power TYPE c LENGTH 1. + TYPES ty_customer TYPE c LENGTH 10. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + TYPES: BEGIN OF ty_rack, + rack_id TYPE c LENGTH 10, + customer TYPE ty_customer, + size_class TYPE ty_size, + power_class TYPE ty_power, + END OF ty_rack. + TYPES tt_racks TYPE STANDARD TABLE OF ty_rack WITH EMPTY KEY. + TYPES: BEGIN OF ty_bill, + customer TYPE ty_customer, + rack_count TYPE i, + total_fee TYPE ty_fee, + END OF ty_bill. + TYPES tt_bills TYPE STANDARD TABLE OF ty_bill WITH EMPTY KEY. +- Public method: + METHODS calculate + IMPORTING it_racks TYPE tt_racks + RETURNING VALUE(rt_bills) TYPE tt_bills. +- Do not add other public methods. + +# 5. Business rules +A rack is valid when its size class is S, M or L and its power class is A, B +or C. +1. Read it_racks in the given order. +2. A rack ID identifies one rack. If a rack ID was already read, skip the rack. + Remember the rack ID of every rack that you read, also when the rack is not + valid. +3. If the size class is not S, M or L, or the power class is not A, B or C, + skip the rack. +4. The monthly fee of a valid rack is the base fee of its size class plus the + surcharge of its power class: + - base fee: S = 50.00, M = 120.00, L = 250.00 + - power surcharge: A = 0.00, B = 30.00, C = 70.00 +5. Add the fee of the rack to the total fee of its customer. Increase the rack + count of the customer by 1. +6. After all racks, apply a volume discount to the total fee of each customer: + - 20 or more racks: 20 percent + - 10 or more racks: 10 percent + - less than 10 racks: no discount +7. The result has one row for each customer with at least one valid rack. The + row contains the customer, the number of valid racks and the total fee after + the discount. +8. Sort the result by customer in ascending order. +9. If it_racks is empty, 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: no other object. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1053/task.json b/tasks_gen/train/G1053/task.json new file mode 100644 index 0000000..2b23c90 --- /dev/null +++ b/tasks_gen/train/G1053/task.json @@ -0,0 +1,42 @@ +{ + "id": "G1053", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}RACK_BILLING" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the rack billing class" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}RACK_BILLING", + "file": "reference/rack_billing.clas.abap", + "description": "Rack billing with price lookup, customer grouping and volume discount", + "testclasses_file": "reference/rack_billing.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "key_access" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1054/faulty/m0_pest_fee.clas.abap b/tasks_gen/train/G1054/faulty/m0_pest_fee.clas.abap new file mode 100644 index 0000000..738499d --- /dev/null +++ b/tasks_gen/train/G1054/faulty/m0_pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross + lv_discount. + IF rv_fee < c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps * c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent = abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_false. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/faulty/m1_pest_fee.clas.abap b/tasks_gen/train/G1054/faulty/m1_pest_fee.clas.abap new file mode 100644 index 0000000..d0296c5 --- /dev/null +++ b/tasks_gen/train/G1054/faulty/m1_pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross - lv_discount. + IF rv_fee >= c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps * c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent = abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_false. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/faulty/m2_pest_fee.clas.abap b/tasks_gen/train/G1054/faulty/m2_pest_fee.clas.abap new file mode 100644 index 0000000..87e64f6 --- /dev/null +++ b/tasks_gen/train/G1054/faulty/m2_pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross - lv_discount. + IF rv_fee < c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps / c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent = abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_false. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/faulty/m3_pest_fee.clas.abap b/tasks_gen/train/G1054/faulty/m3_pest_fee.clas.abap new file mode 100644 index 0000000..6bd10fc --- /dev/null +++ b/tasks_gen/train/G1054/faulty/m3_pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross - lv_discount. + IF rv_fee < c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps * c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent <> abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_false. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/faulty/m4_pest_fee.clas.abap b/tasks_gen/train/G1054/faulty/m4_pest_fee.clas.abap new file mode 100644 index 0000000..ac5cb4d --- /dev/null +++ b/tasks_gen/train/G1054/faulty/m4_pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross - lv_discount. + IF rv_fee < c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps * c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent = abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_true. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/generation.json b/tasks_gen/train/G1054/generation.json new file mode 100644 index 0000000..f3483bc --- /dev/null +++ b/tasks_gen/train/G1054/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1054", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1054/hidden/pest_test_hidden.clas.abap b/tasks_gen/train/G1054/hidden/pest_test_hidden.clas.abap new file mode 100644 index 0000000..126498e --- /dev/null +++ b/tasks_gen/train/G1054/hidden/pest_test_hidden.clas.abap @@ -0,0 +1,141 @@ +CLASS {{p}}pest_test_hidden DEFINITION + PUBLIC + FINAL + CREATE PUBLIC + FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pest_fee. + + METHODS setup. + METHODS expect_fee + IMPORTING iv_expected TYPE {{p}}pest_fee=>ty_fee + iv_actual TYPE {{p}}pest_fee=>ty_fee + iv_case TYPE string. + + METHODS rats_base_fee FOR TESTING. + METHODS mice_base_fee FOR TESTING. + METHODS wasp_base_fee FOR TESTING. + METHODS unknown_pest_zero FOR TESTING. + METHODS area_surcharge_step FOR TESTING. + METHODS area_surcharge_rounds FOR TESTING. + METHODS no_surcharge_at_100 FOR TESTING. + METHODS urgent_surcharge FOR TESTING. + METHODS contract_discount FOR TESTING. + METHODS discount_excludes_urgent FOR TESTING. + METHODS minimum_fee FOR TESTING. +ENDCLASS. + + +CLASS {{p}}pest_test_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}pest_fee( ). + ENDMETHOD. + + METHOD expect_fee. + cl_abap_unit_assert=>assert_equals( exp = iv_expected + act = iv_actual + msg = iv_case ). + ENDMETHOD. + + METHOD rats_base_fee. + expect_fee( iv_expected = 120 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'RATS' + iv_area = 50 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'RATS base fee' ). + ENDMETHOD. + + METHOD mice_base_fee. + expect_fee( iv_expected = 55 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'MICE' + iv_area = 0 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'MICE base fee' ). + ENDMETHOD. + + METHOD wasp_base_fee. + expect_fee( iv_expected = 150 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'WASP' + iv_area = 100 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'WASP base fee' ). + ENDMETHOD. + + METHOD unknown_pest_zero. + expect_fee( iv_expected = 0 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'BIRD' + iv_area = 300 + iv_urgent = abap_true + iv_contract = abap_true ) + iv_case = 'unknown pest type' ). + ENDMETHOD. + + METHOD area_surcharge_step. + expect_fee( iv_expected = 135 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'RATS' + iv_area = 101 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'one started area step' ). + ENDMETHOD. + + METHOD area_surcharge_rounds. + expect_fee( iv_expected = 165 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'RATS' + iv_area = 250 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'three started area steps' ). + ENDMETHOD. + + METHOD no_surcharge_at_100. + expect_fee( iv_expected = 80 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'ANTS' + iv_area = 100 + iv_urgent = abap_false + iv_contract = abap_false ) + iv_case = 'no area surcharge at 100' ). + ENDMETHOD. + + METHOD urgent_surcharge. + expect_fee( iv_expected = 125 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'ANTS' + iv_area = 100 + iv_urgent = abap_true + iv_contract = abap_false ) + iv_case = 'urgent surcharge' ). + ENDMETHOD. + + METHOD contract_discount. + expect_fee( iv_expected = 135 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'RATS' + iv_area = 200 + iv_urgent = abap_false + iv_contract = abap_true ) + iv_case = 'contract discount' ). + ENDMETHOD. + + METHOD discount_excludes_urgent. + expect_fee( iv_expected = 153 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'RATS' + iv_area = 0 + iv_urgent = abap_true + iv_contract = abap_true ) + iv_case = 'discount without urgent surcharge' ). + ENDMETHOD. + + METHOD minimum_fee. + expect_fee( iv_expected = 50 + iv_actual = mo_cut->calculate_fee( iv_pest_type = 'MICE' + iv_area = 0 + iv_urgent = abap_false + iv_contract = abap_true ) + iv_case = 'minimum fee' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/mutation.json b/tasks_gen/train/G1054/mutation.json new file mode 100644 index 0000000..494cc88 --- /dev/null +++ b/tasks_gen/train/G1054/mutation.json @@ -0,0 +1,85 @@ +{ + "task": "G1054", + "mutants": [ + { + "object": "{{P}}PEST_FEE", + "mutant": "line 60: - -> + (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "CONTRACT_DISCOUNT", + "DISCOUNT_EXCLUDES_URGENT", + "MINIMUM_FEE" + ] + }, + { + "object": "{{P}}PEST_FEE", + "mutant": "line 61: < -> >= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "AREA_SURCHARGE_ROUNDS", + "AREA_SURCHARGE_STEP", + "CONTRACT_DISCOUNT", + "DISCOUNT_EXCLUDES_URGENT", + "MICE_BASE_FEE", + "MINIMUM_FEE", + "NO_SURCHARGE_AT_100", + "RATS_BASE_FEE", + "URGENT_SURCHARGE", + "WASP_BASE_FEE" + ] + }, + { + "object": "{{P}}PEST_FEE", + "mutant": "line 88: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "AREA_SURCHARGE_ROUNDS", + "AREA_SURCHARGE_STEP", + "CONTRACT_DISCOUNT" + ] + }, + { + "object": "{{P}}PEST_FEE", + "mutant": "line 92: = -> <> (eq)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "AREA_SURCHARGE_ROUNDS", + "AREA_SURCHARGE_STEP", + "CONTRACT_DISCOUNT", + "DISCOUNT_EXCLUDES_URGENT", + "MICE_BASE_FEE", + "MINIMUM_FEE", + "NO_SURCHARGE_AT_100", + "RATS_BASE_FEE", + "URGENT_SURCHARGE", + "WASP_BASE_FEE" + ] + }, + { + "object": "{{P}}PEST_FEE", + "mutant": "line 98: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "AREA_SURCHARGE_ROUNDS", + "AREA_SURCHARGE_STEP", + "CONTRACT_DISCOUNT", + "DISCOUNT_EXCLUDES_URGENT", + "MICE_BASE_FEE", + "MINIMUM_FEE", + "NO_SURCHARGE_AT_100", + "RATS_BASE_FEE", + "URGENT_SURCHARGE", + "WASP_BASE_FEE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1054/reference/pest_fee.clas.abap b/tasks_gen/train/G1054/reference/pest_fee.clas.abap new file mode 100644 index 0000000..536d625 --- /dev/null +++ b/tasks_gen/train/G1054/reference/pest_fee.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_minimum_fee TYPE ty_fee VALUE '50.00', + c_urgent_fee TYPE ty_fee VALUE '45.00', + c_area_free TYPE i VALUE 100, + c_area_step TYPE i VALUE 50, + c_area_step_fee TYPE i VALUE 15, + c_discount_pct TYPE i VALUE 10. + + METHODS base_fee + IMPORTING iv_pest_type TYPE ty_pest + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS area_surcharge + IMPORTING iv_area TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS urgent_surcharge + IMPORTING iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + METHODS contract_discount + IMPORTING iv_base TYPE ty_fee + iv_area_fee TYPE ty_fee + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA(lv_base) = base_fee( iv_pest_type ). + IF lv_base = 0. + CLEAR rv_fee. + RETURN. + ENDIF. + + DATA(lv_area_fee) = area_surcharge( iv_area ). + DATA(lv_gross) = lv_base + lv_area_fee + urgent_surcharge( iv_urgent ). + DATA(lv_discount) = contract_discount( iv_base = lv_base + iv_area_fee = lv_area_fee + iv_contract = iv_contract ). + + rv_fee = lv_gross - lv_discount. + IF rv_fee < c_minimum_fee. + rv_fee = c_minimum_fee. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_pest_type. + WHEN 'RATS'. + rv_fee = 120. + WHEN 'MICE'. + rv_fee = 55. + WHEN 'WASP'. + rv_fee = 150. + WHEN 'ANTS'. + rv_fee = 80. + WHEN OTHERS. + CLEAR rv_fee. + ENDCASE. + ENDMETHOD. + + METHOD area_surcharge. + DATA(lv_billable) = iv_area - c_area_free. + IF lv_billable <= 0. + RETURN. + ENDIF. + + DATA(lv_steps) = ( lv_billable + c_area_step - 1 ) DIV c_area_step. + rv_fee = lv_steps * c_area_step_fee. + ENDMETHOD. + + METHOD urgent_surcharge. + IF iv_urgent = abap_true. + rv_fee = c_urgent_fee. + ENDIF. + ENDMETHOD. + + METHOD contract_discount. + IF iv_contract = abap_false. + RETURN. + ENDIF. + + DATA(lv_discountable) = iv_base + iv_area_fee. + rv_fee = lv_discountable * c_discount_pct / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/reference/pest_fee.testclasses.abap b/tasks_gen/train/G1054/reference/pest_fee.testclasses.abap new file mode 100644 index 0000000..712a210 --- /dev/null +++ b/tasks_gen/train/G1054/reference/pest_fee.testclasses.abap @@ -0,0 +1,73 @@ +CLASS ltc_pest_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pest_fee. + + METHODS setup. + METHODS rats_with_area FOR TESTING. + METHODS mice_base_fee FOR TESTING. + METHODS unknown_pest_is_free FOR TESTING. + METHODS urgent_job FOR TESTING. + METHODS contract_discount FOR TESTING. + METHODS minimum_fee FOR TESTING. +ENDCLASS. + + +CLASS ltc_pest_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}pest_fee( ). + ENDMETHOD. + + METHOD rats_with_area. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 135. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'RATS' iv_area = 101 + iv_urgent = abap_false iv_contract = abap_false ) ). + ENDMETHOD. + + METHOD mice_base_fee. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 55. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'MICE' iv_area = 0 + iv_urgent = abap_false iv_contract = abap_false ) ). + ENDMETHOD. + + METHOD unknown_pest_is_free. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 0. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'BIRD' iv_area = 500 + iv_urgent = abap_true iv_contract = abap_true ) ). + ENDMETHOD. + + METHOD urgent_job. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 125. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'ANTS' iv_area = 100 + iv_urgent = abap_true iv_contract = abap_false ) ). + ENDMETHOD. + + METHOD contract_discount. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 153. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'RATS' iv_area = 0 + iv_urgent = abap_true iv_contract = abap_true ) ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_exp TYPE {{p}}pest_fee=>ty_fee. + lv_exp = 50. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = mo_cut->calculate_fee( iv_pest_type = 'MICE' iv_area = 0 + iv_urgent = abap_false iv_contract = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/seed/pest_fee.clas.abap b/tasks_gen/train/G1054/seed/pest_fee.clas.abap new file mode 100644 index 0000000..22c6285 --- /dev/null +++ b/tasks_gen/train/G1054/seed/pest_fee.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}pest_fee DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_pest TYPE c LENGTH 4. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate_fee + IMPORTING iv_pest_type TYPE ty_pest + iv_area TYPE i + iv_urgent TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}pest_fee IMPLEMENTATION. + METHOD calculate_fee. + DATA lv_base TYPE ty_fee. + DATA lv_area_fee TYPE ty_fee. + DATA lv_area_fee2 TYPE ty_fee. + DATA lv_total TYPE ty_fee. + DATA lv_discount TYPE ty_fee. + DATA lv_steps TYPE i. + DATA lv_rest TYPE i. + DATA lv_dummy TYPE i. + + " get the base fee + IF iv_pest_type = 'RATS'. + lv_base = 120. + ELSEIF iv_pest_type = 'MICE'. + lv_base = 55. + ELSEIF iv_pest_type = 'WASP'. + lv_base = 150. + ELSEIF iv_pest_type = 'ANTS'. + lv_base = 80. + ELSE. + lv_base = 0. + ENDIF. + + IF lv_base = 0. + rv_fee = 0. + RETURN. + ENDIF. + + " area surcharge + lv_area_fee = 0. + IF iv_area > 100. + lv_rest = iv_area - 100. + lv_steps = lv_rest DIV 50. + IF lv_rest MOD 50 <> 0. + lv_steps = lv_steps + 1. + ENDIF. + lv_area_fee = lv_steps * 15. + ENDIF. + + lv_total = lv_base + lv_area_fee. + + " urgent jobs cost more + IF iv_urgent = 'X'. + lv_total = lv_total + 45. + ENDIF. + + " contract customers get a discount + IF iv_contract = 'X'. + lv_area_fee2 = 0. + IF iv_area > 100. + lv_rest = iv_area - 100. + lv_steps = lv_rest DIV 50. + IF lv_rest MOD 50 <> 0. + lv_steps = lv_steps + 1. + ENDIF. + lv_area_fee2 = lv_steps * 15. + ENDIF. + lv_discount = ( lv_base + lv_area_fee2 ) * 10 / 100. + lv_total = lv_total - lv_discount. + ENDIF. + + IF lv_total < 50. + lv_total = 50. + ENDIF. + + rv_fee = lv_total. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1054/spec.md b/tasks_gen/train/G1054/spec.md new file mode 100644 index 0000000..c085a58 --- /dev/null +++ b/tasks_gen/train/G1054/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A pest control company charges a fee for each treatment visit. The dispatcher +calls the class {{P}}PEST_FEE for each job before it plans the route. The class +is legacy code. Refactor the class in place. The class name and the public +interface stay the same. The fee stays the same for each input. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}PEST_FEE exists in package $TMP. It is active. +- The class is public, final, and has a public constructor without parameters. +- The public interface has the types TY_PEST and TY_FEE and the method + CALCULATE_FEE with the importing parameters IV_PEST_TYPE, IV_AREA, IV_URGENT + and IV_CONTRACT and the returning parameter RV_FEE. +- Users report that the class is difficult to read and to maintain. The method + is long, the numbers have no name, the same area logic appears twice, and one + variable is not used. +- The legacy implementation contains the business rules. Read it before you + change it. + +# 4. Contract +- Refactor the class {{P}}PEST_FEE in package $TMP. +- The class name stays the same. +- The class stays public and final and keeps the public constructor without + parameters. +- The public types TY_PEST and TY_FEE stay the same. +- The public method CALCULATE_FEE keeps its name, its importing parameters + IV_PEST_TYPE, IV_AREA, IV_URGENT, IV_CONTRACT and its returning parameter + RV_FEE. +- Do not add other public methods. +- Do not change the behavior. + +# 5. Business rules +- The legacy implementation of {{P}}PEST_FEE defines the business rules. +- The refactored class must return the same fee as the legacy class for every + combination of the input values. +- The rules are not repeated in this document. Read the legacy code. + +# 6. Constraints +- Release target: SAP_BASIS 8.16 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No number + without a name. No deep nesting. No unused variable. No comment that restates + the code. +- Out of scope: do not change the public interface of {{P}}PEST_FEE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- The class has no ATC finding with priority 1 or 2. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1054/task.json b/tasks_gen/train/G1054/task.json new file mode 100644 index 0000000..7244009 --- /dev/null +++ b/tasks_gen/train/G1054/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1054", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}PEST_FEE", + "file": "seed/pest_fee.clas.abap", + "description": "Legacy class that calculates the fee of a pest control visit" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PEST_FEE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}PEST_TEST_HIDDEN", + "file": "hidden/pest_test_hidden.clas.abap", + "description": "Hidden tests for the unchanged fee behavior" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PEST_FEE", + "file": "reference/pest_fee.clas.abap", + "description": "Refactored pest control fee calculation", + "testclasses_file": "reference/pest_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "magic_numbers" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1056/faulty/m0_tyre_checker.clas.abap b/tasks_gen/train/G1056/faulty/m0_tyre_checker.clas.abap new file mode 100644 index 0000000..7c8869d --- /dev/null +++ b/tasks_gen/train/G1056/faulty/m0_tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty >= 1 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm <> ms_vehicle-width_mm + OR is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index < ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/faulty/m1_tyre_checker.clas.abap b/tasks_gen/train/G1056/faulty/m1_tyre_checker.clas.abap new file mode 100644 index 0000000..a7eae46 --- /dev/null +++ b/tasks_gen/train/G1056/faulty/m1_tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty < 2 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm <> ms_vehicle-width_mm + OR is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index < ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/faulty/m2_tyre_checker.clas.abap b/tasks_gen/train/G1056/faulty/m2_tyre_checker.clas.abap new file mode 100644 index 0000000..de82718 --- /dev/null +++ b/tasks_gen/train/G1056/faulty/m2_tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty < 1 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm = ms_vehicle-width_mm + OR is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index < ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/faulty/m3_tyre_checker.clas.abap b/tasks_gen/train/G1056/faulty/m3_tyre_checker.clas.abap new file mode 100644 index 0000000..f150212 --- /dev/null +++ b/tasks_gen/train/G1056/faulty/m3_tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty < 1 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm <> ms_vehicle-width_mm + AND is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index < ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/faulty/m4_tyre_checker.clas.abap b/tasks_gen/train/G1056/faulty/m4_tyre_checker.clas.abap new file mode 100644 index 0000000..d1121a0 --- /dev/null +++ b/tasks_gen/train/G1056/faulty/m4_tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty < 1 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm <> ms_vehicle-width_mm + OR is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index >= ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/generation.json b/tasks_gen/train/G1056/generation.json new file mode 100644 index 0000000..7ac4925 --- /dev/null +++ b/tasks_gen/train/G1056/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1056", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "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 +} \ No newline at end of file diff --git a/tasks_gen/train/G1056/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1056/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..95681ce --- /dev/null +++ b/tasks_gen/train/G1056/hidden/t20_hidden.clas.abap @@ -0,0 +1,182 @@ +CLASS {{p}}t20_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}tyre_checker. + + METHODS setup. + METHODS accepts_valid_order FOR TESTING. + METHODS accepts_higher_ratings FOR TESTING. + METHODS rejects_zero_qty FOR TESTING. + METHODS rejects_five_tyres FOR TESTING. + METHODS rejects_wrong_width FOR TESTING. + METHODS rejects_wrong_profile FOR TESTING. + METHODS rejects_wrong_rim FOR TESTING. + METHODS rejects_low_load FOR TESTING. + METHODS rejects_low_speed FOR TESTING. + METHODS qty_checked_first FOR TESTING. + METHODS size_checked_before_load FOR TESTING. + METHODS load_checked_before_speed FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tyre_checker( + is_vehicle = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 ) ). + ENDMETHOD. + + METHOD accepts_valid_order. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 1 ) ). + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 4 ) ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( |Unexpected exception: { lo_error->mv_text }| ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_higher_ratings. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 95 speed_kmh = 270 qty = 2 ) ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( |Unexpected exception: { lo_error->mv_text }| ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_zero_qty. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 0 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Quantity must be between 1 and 4` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_five_tyres. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 5 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Quantity must be between 1 and 4` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_wrong_width. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 195 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Tyre size does not match the vehicle` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_wrong_profile. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 60 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Tyre size does not match the vehicle` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_wrong_rim. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 15 + load_index = 91 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Tyre size does not match the vehicle` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_load. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 88 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Load index is too low` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_speed. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 210 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Speed rating is too low` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD qty_checked_first. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 999 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 0 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Quantity must be between 1 and 4` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD size_checked_before_load. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 999 profile_pct = 55 rim_inch = 16 + load_index = 50 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Tyre size does not match the vehicle` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD load_checked_before_speed. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 50 speed_kmh = 100 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Load index is too low` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/mutation.json b/tasks_gen/train/G1056/mutation.json new file mode 100644 index 0000000..3d1c1c6 --- /dev/null +++ b/tasks_gen/train/G1056/mutation.json @@ -0,0 +1,76 @@ +{ + "task": "G1056", + "mutants": [ + { + "object": "{{P}}TYRE_CHECKER", + "mutant": "line 48: < -> >= (rel)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "ACCEPTS_HIGHER_RATINGS", + "ACCEPTS_VALID_ORDER", + "LOAD_CHECKED_BEFORE_SPEED", + "QTY_CHECKED_FIRST", + "REJECTS_LOW_LOAD", + "REJECTS_LOW_SPEED", + "REJECTS_WRONG_PROFILE", + "REJECTS_WRONG_RIM", + "REJECTS_WRONG_WIDTH", + "REJECTS_ZERO_QTY", + "SIZE_CHECKED_BEFORE_LOAD" + ] + }, + { + "object": "{{P}}TYRE_CHECKER", + "mutant": "line 48: 1 -> 2 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "ACCEPTS_VALID_ORDER" + ] + }, + { + "object": "{{P}}TYRE_CHECKER", + "mutant": "line 54: <> -> = (rel)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "ACCEPTS_HIGHER_RATINGS", + "ACCEPTS_VALID_ORDER", + "LOAD_CHECKED_BEFORE_SPEED", + "REJECTS_LOW_LOAD", + "REJECTS_LOW_SPEED", + "REJECTS_WRONG_WIDTH", + "SIZE_CHECKED_BEFORE_LOAD" + ] + }, + { + "object": "{{P}}TYRE_CHECKER", + "mutant": "line 55: OR -> AND (logic)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "REJECTS_WRONG_PROFILE", + "REJECTS_WRONG_WIDTH", + "SIZE_CHECKED_BEFORE_LOAD" + ] + }, + { + "object": "{{P}}TYRE_CHECKER", + "mutant": "line 62: < -> >= (rel)", + "status": "killed", + "hidden": "7/12", + "failed_tests": [ + "ACCEPTS_HIGHER_RATINGS", + "ACCEPTS_VALID_ORDER", + "LOAD_CHECKED_BEFORE_SPEED", + "REJECTS_LOW_LOAD", + "REJECTS_LOW_SPEED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1056/reference/cx_tyre_error.clas.abap b/tasks_gen/train/G1056/reference/cx_tyre_error.clas.abap new file mode 100644 index 0000000..17ce96d --- /dev/null +++ b/tasks_gen/train/G1056/reference/cx_tyre_error.clas.abap @@ -0,0 +1,19 @@ +CLASS {{p}}cx_tyre_error DEFINITION + PUBLIC + INHERITING FROM cx_static_check + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_text TYPE string. + + DATA mv_text TYPE string READ-ONLY. +ENDCLASS. + + +CLASS {{p}}cx_tyre_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/cx_tyre_load.clas.abap b/tasks_gen/train/G1056/reference/cx_tyre_load.clas.abap new file mode 100644 index 0000000..2d255a6 --- /dev/null +++ b/tasks_gen/train/G1056/reference/cx_tyre_load.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_tyre_load DEFINITION + PUBLIC + INHERITING FROM {{p}}cx_tyre_error + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_tyre_load IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = 'Load index is too low' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/cx_tyre_qty.clas.abap b/tasks_gen/train/G1056/reference/cx_tyre_qty.clas.abap new file mode 100644 index 0000000..61d309d --- /dev/null +++ b/tasks_gen/train/G1056/reference/cx_tyre_qty.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_tyre_qty DEFINITION + PUBLIC + INHERITING FROM {{p}}cx_tyre_error + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_tyre_qty IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = 'Quantity must be between 1 and 4' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/cx_tyre_size.clas.abap b/tasks_gen/train/G1056/reference/cx_tyre_size.clas.abap new file mode 100644 index 0000000..71e79af --- /dev/null +++ b/tasks_gen/train/G1056/reference/cx_tyre_size.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_tyre_size DEFINITION + PUBLIC + INHERITING FROM {{p}}cx_tyre_error + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_tyre_size IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = 'Tyre size does not match the vehicle' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/cx_tyre_speed.clas.abap b/tasks_gen/train/G1056/reference/cx_tyre_speed.clas.abap new file mode 100644 index 0000000..482dbb4 --- /dev/null +++ b/tasks_gen/train/G1056/reference/cx_tyre_speed.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_tyre_speed DEFINITION + PUBLIC + INHERITING FROM {{p}}cx_tyre_error + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_tyre_speed IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = 'Speed rating is too low' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/tyre_checker.clas.abap b/tasks_gen/train/G1056/reference/tyre_checker.clas.abap new file mode 100644 index 0000000..1375de8 --- /dev/null +++ b/tasks_gen/train/G1056/reference/tyre_checker.clas.abap @@ -0,0 +1,72 @@ +CLASS {{p}}tyre_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING is_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_order + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty + {{p}}cx_tyre_size + {{p}}cx_tyre_load + {{p}}cx_tyre_speed. + + PRIVATE SECTION. + DATA ms_vehicle TYPE {{p}}if_tyre_order=>ty_vehicle. + + METHODS check_qty + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_qty. + METHODS check_size + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_size. + METHODS check_load + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_load. + METHODS check_speed + IMPORTING is_order TYPE {{p}}if_tyre_order=>ty_order + RAISING {{p}}cx_tyre_speed. +ENDCLASS. + + +CLASS {{p}}tyre_checker IMPLEMENTATION. + METHOD constructor. + ms_vehicle = is_vehicle. + ENDMETHOD. + + METHOD check_order. + check_qty( is_order ). + check_size( is_order ). + check_load( is_order ). + check_speed( is_order ). + ENDMETHOD. + + METHOD check_qty. + IF is_order-qty < 1 OR is_order-qty > 4. + RAISE EXCEPTION TYPE {{p}}cx_tyre_qty. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF is_order-width_mm <> ms_vehicle-width_mm + OR is_order-profile_pct <> ms_vehicle-profile_pct + OR is_order-rim_inch <> ms_vehicle-rim_inch. + RAISE EXCEPTION TYPE {{p}}cx_tyre_size. + ENDIF. + ENDMETHOD. + + METHOD check_load. + IF is_order-load_index < ms_vehicle-load_index. + RAISE EXCEPTION TYPE {{p}}cx_tyre_load. + ENDIF. + ENDMETHOD. + + METHOD check_speed. + IF is_order-speed_kmh < ms_vehicle-speed_kmh. + RAISE EXCEPTION TYPE {{p}}cx_tyre_speed. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/reference/tyre_checker.testclasses.abap b/tasks_gen/train/G1056/reference/tyre_checker.testclasses.abap new file mode 100644 index 0000000..98088b9 --- /dev/null +++ b/tasks_gen/train/G1056/reference/tyre_checker.testclasses.abap @@ -0,0 +1,96 @@ +CLASS ltc_tyre_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}tyre_checker. + + METHODS setup. + METHODS accepts_valid_order FOR TESTING. + METHODS rejects_bad_qty FOR TESTING. + METHODS rejects_wrong_size FOR TESTING. + METHODS rejects_low_load FOR TESTING. + METHODS rejects_low_speed FOR TESTING. + METHODS reports_qty_message FOR TESTING. +ENDCLASS. + + +CLASS ltc_tyre_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tyre_checker( + is_vehicle = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 ) ). + ENDMETHOD. + + METHOD accepts_valid_order. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 4 ) ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( |Unexpected exception: { lo_error->mv_text }| ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_bad_qty. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 0 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Quantity must be between 1 and 4` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_wrong_size. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 60 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Tyre size does not match the vehicle` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_load. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 90 speed_kmh = 240 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Load index is too low` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_speed. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 230 qty = 2 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Speed rating is too low` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. + + METHOD reports_qty_message. + TRY. + mo_cut->check_order( + is_order = VALUE #( width_mm = 205 profile_pct = 55 rim_inch = 16 + load_index = 91 speed_kmh = 240 qty = 5 ) ). + cl_abap_unit_assert=>fail( `An exception is expected` ). + CATCH {{p}}cx_tyre_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = `Quantity must be between 1 and 4` + act = lo_error->mv_text ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1056/seed/if_tyre_order.intf.abap b/tasks_gen/train/G1056/seed/if_tyre_order.intf.abap new file mode 100644 index 0000000..2e37e39 --- /dev/null +++ b/tasks_gen/train/G1056/seed/if_tyre_order.intf.abap @@ -0,0 +1,18 @@ +INTERFACE {{p}}if_tyre_order PUBLIC. + TYPES: + BEGIN OF ty_vehicle, + width_mm TYPE i, + profile_pct TYPE i, + rim_inch TYPE i, + load_index TYPE i, + speed_kmh TYPE i, + END OF ty_vehicle, + BEGIN OF ty_order, + width_mm TYPE i, + profile_pct TYPE i, + rim_inch TYPE i, + load_index TYPE i, + speed_kmh TYPE i, + qty TYPE i, + END OF ty_order. +ENDINTERFACE. diff --git a/tasks_gen/train/G1056/spec.md b/tasks_gen/train/G1056/spec.md new file mode 100644 index 0000000..a3da62c --- /dev/null +++ b/tasks_gen/train/G1056/spec.md @@ -0,0 +1,77 @@ +# 1. Goal +A tyre shop fits tyres to a vehicle. Before the mechanic starts a job, the +shop checks the fitting order against the vehicle. The shop needs one reason +for each rejected order. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_TYRE_ORDER exists in package $TMP. It is active. +- The interface has two types: + - TY_VEHICLE with the components WIDTH_MM, PROFILE_PCT, RIM_INCH, + LOAD_INDEX and SPEED_KMH. All components have the type i. + - TY_ORDER with the components WIDTH_MM, PROFILE_PCT, RIM_INCH, LOAD_INDEX, + SPEED_KMH and QTY. All components have the type i. +- WIDTH_MM is the tyre width in millimetres. PROFILE_PCT is the aspect ratio + in percent. RIM_INCH is the rim diameter in inches. LOAD_INDEX is the load + index of the tyre. SPEED_KMH is the maximum speed in kilometres per hour. + QTY is the number of tyres of the order. + +# 4. Contract +Create these objects in package $TMP. + +- Class {{P}}TYRE_CHECKER. It is public and final. + - Public constructor: + CONSTRUCTOR IMPORTING is_vehicle TYPE {{P}}IF_TYRE_ORDER=>TY_VEHICLE. + - Public method: + CHECK_ORDER IMPORTING is_order TYPE {{P}}IF_TYRE_ORDER=>TY_ORDER + RAISING {{P}}CX_TYRE_QTY {{P}}CX_TYRE_SIZE {{P}}CX_TYRE_LOAD + {{P}}CX_TYRE_SPEED. +- Exception class {{P}}CX_TYRE_ERROR. It inherits from CX_STATIC_CHECK. + - Public read-only attribute MV_TEXT TYPE string. + - Public constructor: + CONSTRUCTOR IMPORTING iv_text TYPE string. +- Exception class {{P}}CX_TYRE_QTY. It inherits from {{P}}CX_TYRE_ERROR. + - Public constructor without parameters. +- Exception class {{P}}CX_TYRE_SIZE. It inherits from {{P}}CX_TYRE_ERROR. + - Public constructor without parameters. +- Exception class {{P}}CX_TYRE_LOAD. It inherits from {{P}}CX_TYRE_ERROR. + - Public constructor without parameters. +- Exception class {{P}}CX_TYRE_SPEED. It inherits from {{P}}CX_TYRE_ERROR. + - Public constructor without parameters. + +Do not add other public methods to {{P}}TYRE_CHECKER. + +# 5. Business rules +CHECK_ORDER compares the order with the vehicle of the constructor. Check the +rules in this sequence. Raise the exception of the first rule that fails. Do +not check a rule after a rule has failed. + +1. QTY must be greater than or equal to 1 and less than or equal to 4. If not, + raise {{P}}CX_TYRE_QTY. +2. WIDTH_MM, PROFILE_PCT and RIM_INCH of the order must be equal to the + WIDTH_MM, PROFILE_PCT and RIM_INCH of the vehicle. If not, raise + {{P}}CX_TYRE_SIZE. +3. LOAD_INDEX of the order must be greater than or equal to the LOAD_INDEX of + the vehicle. If not, raise {{P}}CX_TYRE_LOAD. +4. SPEED_KMH of the order must be greater than or equal to the SPEED_KMH of + the vehicle. If not, raise {{P}}CX_TYRE_SPEED. +5. If no rule fails, the method returns without an exception. + +Each exception class sets MV_TEXT in its own constructor: +- {{P}}CX_TYRE_QTY: 'Quantity must be between 1 and 4' +- {{P}}CX_TYRE_SIZE: 'Tyre size does not match the vehicle' +- {{P}}CX_TYRE_LOAD: 'Load index is too low' +- {{P}}CX_TYRE_SPEED: 'Speed rating is too low' + +# 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_TYRE_ORDER. + +# 7. Acceptance +- All objects are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1056/task.json b/tasks_gen/train/G1056/task.json new file mode 100644 index 0000000..8bda59f --- /dev/null +++ b/tasks_gen/train/G1056/task.json @@ -0,0 +1,100 @@ +{ + "id": "G1056", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_TYRE_ORDER", + "file": "seed/if_tyre_order.intf.abap", + "description": "Types for the fitting order and the vehicle" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TYRE_CHECKER" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_QTY" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_SIZE" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_LOAD" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_SPEED" + } + ], + "out_of_scope": [ + "{{P}}IF_TYRE_ORDER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "T20 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_ERROR", + "file": "reference/cx_tyre_error.clas.abap", + "description": "Exception superclass with a message text" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_QTY", + "file": "reference/cx_tyre_qty.clas.abap", + "description": "Invalid quantity" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_SIZE", + "file": "reference/cx_tyre_size.clas.abap", + "description": "Tyre size does not fit the vehicle" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_LOAD", + "file": "reference/cx_tyre_load.clas.abap", + "description": "Load index too low" + }, + { + "type": "CLAS", + "name": "{{P}}CX_TYRE_SPEED", + "file": "reference/cx_tyre_speed.clas.abap", + "description": "Speed rating too low" + }, + { + "type": "CLAS", + "name": "{{P}}TYRE_CHECKER", + "file": "reference/tyre_checker.clas.abap", + "description": "Checks a tyre fitting order against a vehicle", + "testclasses_file": "reference/tyre_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1057/faulty/m0_air_freight_pricer.clas.abap b/tasks_gen/train/G1057/faulty/m0_air_freight_pricer.clas.abap new file mode 100644 index 0000000..8009545 --- /dev/null +++ b/tasks_gen/train/G1057/faulty/m0_air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous <> abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee + rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category <> {{p}}if_air_freight=>c_category-general + AND iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category <> {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/faulty/m1_air_freight_pricer.clas.abap b/tasks_gen/train/G1057/faulty/m1_air_freight_pricer.clas.abap new file mode 100644 index 0000000..b1915a4 --- /dev/null +++ b/tasks_gen/train/G1057/faulty/m1_air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous = abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee - rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category <> {{p}}if_air_freight=>c_category-general + AND iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category <> {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/faulty/m2_air_freight_pricer.clas.abap b/tasks_gen/train/G1057/faulty/m2_air_freight_pricer.clas.abap new file mode 100644 index 0000000..eaffa68 --- /dev/null +++ b/tasks_gen/train/G1057/faulty/m2_air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous = abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee + rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category = {{p}}if_air_freight=>c_category-general + AND iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category <> {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/faulty/m3_air_freight_pricer.clas.abap b/tasks_gen/train/G1057/faulty/m3_air_freight_pricer.clas.abap new file mode 100644 index 0000000..6a8251d --- /dev/null +++ b/tasks_gen/train/G1057/faulty/m3_air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous = abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee + rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category <> {{p}}if_air_freight=>c_category-general + OR iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category <> {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/faulty/m4_air_freight_pricer.clas.abap b/tasks_gen/train/G1057/faulty/m4_air_freight_pricer.clas.abap new file mode 100644 index 0000000..53db436 --- /dev/null +++ b/tasks_gen/train/G1057/faulty/m4_air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous = abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee + rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category <> {{p}}if_air_freight=>c_category-general + AND iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category = {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/generation.json b/tasks_gen/train/G1057/generation.json new file mode 100644 index 0000000..bd1e390 --- /dev/null +++ b/tasks_gen/train/G1057/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1057", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/air_frt_hidden.clas.abap line 8: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/air_freight_pricer.testclasses.abap line 8: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1057/hidden/air_frt_hidden.clas.abap b/tasks_gen/train/G1057/hidden/air_frt_hidden.clas.abap new file mode 100644 index 0000000..0fc78a0 --- /dev/null +++ b/tasks_gen/train/G1057/hidden/air_frt_hidden.clas.abap @@ -0,0 +1,179 @@ +CLASS {{p}}air_frt_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_expected TYPE c LENGTH 20. + + DATA mo_cut TYPE REF TO {{p}}if_air_freight. + + METHODS setup. + METHODS assert_weight + IMPORTING iv_expected TYPE ty_expected + iv_actual TYPE {{p}}if_air_freight=>ty_weight. + METHODS assert_amount + IMPORTING iv_expected TYPE ty_expected + iv_actual TYPE {{p}}if_air_freight=>ty_amount. + + METHODS general_shipment FOR TESTING. + METHODS volumetric_weight FOR TESTING. + METHODS rounds_up_to_kg FOR TESTING. + METHODS rate_break_100 FOR TESTING. + METHODS rate_break_101 FOR TESTING. + METHODS rate_break_501 FOR TESTING. + METHODS perishable_goods FOR TESTING. + METHODS valuable_dangerous FOR TESTING. + METHODS rejects_zero_weight FOR TESTING. + METHODS rejects_zero_volume FOR TESTING. + METHODS rejects_bad_category FOR TESTING. + METHODS rejects_too_heavy FOR TESTING. +ENDCLASS. + + +CLASS {{p}}air_frt_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}air_freight_pricer( ). + ENDMETHOD. + + METHOD assert_weight. + DATA lv_expected TYPE {{p}}if_air_freight=>ty_weight. + lv_expected = iv_expected. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = iv_actual ). + ENDMETHOD. + + METHOD assert_amount. + DATA lv_expected TYPE {{p}}if_air_freight=>ty_amount. + lv_expected = iv_expected. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = iv_actual ). + ENDMETHOD. + + METHOD general_shipment. + DATA(ls_result) = mo_cut->price( iv_weight = '12.000' + iv_volume = '48000.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-ok + act = ls_result-reason ). + assert_weight( iv_expected = '12.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '108.00' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-dangerous_fee ). + assert_amount( iv_expected = '12.96' iv_actual = ls_result-fuel_fee ). + assert_amount( iv_expected = '15.00' iv_actual = ls_result-security_fee ). + assert_amount( iv_expected = '135.96' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD volumetric_weight. + DATA(ls_result) = mo_cut->price( iv_weight = '5.000' + iv_volume = '120000.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-ok + act = ls_result-reason ). + assert_weight( iv_expected = '20.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '180.00' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '21.60' iv_actual = ls_result-fuel_fee ). + assert_amount( iv_expected = '216.60' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rounds_up_to_kg. + DATA(ls_result) = mo_cut->price( iv_weight = '12.400' + iv_volume = '900.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + assert_weight( iv_expected = '13.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '117.00' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '146.04' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rate_break_100. + DATA(ls_result) = mo_cut->price( iv_weight = '100.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + assert_weight( iv_expected = '100.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '720.00' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '821.40' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rate_break_101. + DATA(ls_result) = mo_cut->price( iv_weight = '100.500' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + assert_weight( iv_expected = '101.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '565.60' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '648.47' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rate_break_501. + DATA(ls_result) = mo_cut->price( iv_weight = '500.400' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + assert_weight( iv_expected = '501.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '2154.30' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '2427.82' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD perishable_goods. + DATA(ls_result) = mo_cut->price( iv_weight = '10.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-perishable + iv_dangerous = abap_false ). + assert_amount( iv_expected = '112.50' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-dangerous_fee ). + assert_amount( iv_expected = '141.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD valuable_dangerous. + DATA(ls_result) = mo_cut->price( iv_weight = '10.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-valuable + iv_dangerous = abap_true ). + assert_amount( iv_expected = '135.00' iv_actual = ls_result-freight ). + assert_amount( iv_expected = '54.00' iv_actual = ls_result-dangerous_fee ). + assert_amount( iv_expected = '22.68' iv_actual = ls_result-fuel_fee ). + assert_amount( iv_expected = '226.68' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rejects_zero_weight. + DATA(ls_result) = mo_cut->price( iv_weight = '0.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-bad_weight + act = ls_result-reason ). + assert_weight( iv_expected = '0.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rejects_zero_volume. + DATA(ls_result) = mo_cut->price( iv_weight = '10.000' + iv_volume = '0.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-bad_volume + act = ls_result-reason ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rejects_bad_category. + DATA(ls_result) = mo_cut->price( iv_weight = '10.000' + iv_volume = '600.000' + iv_category = 'EXPRESS' + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-bad_category + act = ls_result-reason ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD rejects_too_heavy. + DATA(ls_result) = mo_cut->price( iv_weight = '3000.500' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-too_heavy + act = ls_result-reason ). + assert_weight( iv_expected = '0.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/mutation.json b/tasks_gen/train/G1057/mutation.json new file mode 100644 index 0000000..b7b2e2c --- /dev/null +++ b/tasks_gen/train/G1057/mutation.json @@ -0,0 +1,84 @@ +{ + "task": "G1057", + "mutants": [ + { + "object": "{{P}}AIR_FREIGHT_PRICER", + "mutant": "line 55: = -> <> (eq)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "GENERAL_SHIPMENT", + "PERISHABLE_GOODS", + "RATE_BREAK_100", + "RATE_BREAK_101", + "RATE_BREAK_501", + "ROUNDS_UP_TO_KG", + "VALUABLE_DANGEROUS", + "VOLUMETRIC_WEIGHT" + ] + }, + { + "object": "{{P}}AIR_FREIGHT_PRICER", + "mutant": "line 61: + -> - (arith)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "GENERAL_SHIPMENT", + "PERISHABLE_GOODS", + "RATE_BREAK_100", + "RATE_BREAK_101", + "RATE_BREAK_501", + "ROUNDS_UP_TO_KG", + "VALUABLE_DANGEROUS", + "VOLUMETRIC_WEIGHT" + ] + }, + { + "object": "{{P}}AIR_FREIGHT_PRICER", + "mutant": "line 70: <> -> = (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "GENERAL_SHIPMENT", + "RATE_BREAK_100", + "RATE_BREAK_101", + "RATE_BREAK_501", + "REJECTS_BAD_CATEGORY", + "REJECTS_TOO_HEAVY", + "ROUNDS_UP_TO_KG", + "VOLUMETRIC_WEIGHT" + ] + }, + { + "object": "{{P}}AIR_FREIGHT_PRICER", + "mutant": "line 71: AND -> OR (logic)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "GENERAL_SHIPMENT", + "PERISHABLE_GOODS", + "RATE_BREAK_100", + "RATE_BREAK_101", + "RATE_BREAK_501", + "REJECTS_TOO_HEAVY", + "ROUNDS_UP_TO_KG", + "VALUABLE_DANGEROUS", + "VOLUMETRIC_WEIGHT" + ] + }, + { + "object": "{{P}}AIR_FREIGHT_PRICER", + "mutant": "line 72: <> -> = (rel)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "REJECTS_BAD_CATEGORY", + "VALUABLE_DANGEROUS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1057/reference/air_freight_pricer.clas.abap b/tasks_gen/train/G1057/reference/air_freight_pricer.clas.abap new file mode 100644 index 0000000..845e681 --- /dev/null +++ b/tasks_gen/train/G1057/reference/air_freight_pricer.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}air_freight_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_air_freight. + + PRIVATE SECTION. + CONSTANTS: + c_volume_divisor TYPE i VALUE 6000, + c_max_weight TYPE {{p}}if_air_freight=>ty_weight VALUE '3000', + c_dangerous_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.4000', + c_fuel_rate TYPE p LENGTH 5 DECIMALS 4 VALUE '0.1200', + c_security_fee TYPE {{p}}if_air_freight=>ty_amount VALUE '15.00'. + + TYPES ty_rate TYPE p LENGTH 7 DECIMALS 2. + + METHODS check_input + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_reason) TYPE {{p}}if_air_freight=>ty_reason. + + METHODS chargeable_weight + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + iv_volume TYPE {{p}}if_air_freight=>ty_volume + RETURNING VALUE(rv_weight) TYPE {{p}}if_air_freight=>ty_weight. + + METHODS base_rate + IMPORTING iv_weight TYPE {{p}}if_air_freight=>ty_weight + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS category_factor + IMPORTING iv_category TYPE {{p}}if_air_freight=>ty_category + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}air_freight_pricer IMPLEMENTATION. + METHOD {{p}}if_air_freight~price. + rs_result-reason = check_input( iv_weight = iv_weight + iv_volume = iv_volume + iv_category = iv_category ). + IF rs_result-reason <> {{p}}if_air_freight=>c_reason-ok. + RETURN. + ENDIF. + + rs_result-chargeable_weight = chargeable_weight( iv_weight = iv_weight + iv_volume = iv_volume ). + IF rs_result-chargeable_weight > c_max_weight. + CLEAR rs_result. + rs_result-reason = {{p}}if_air_freight=>c_reason-too_heavy. + RETURN. + ENDIF. + + rs_result-freight = rs_result-chargeable_weight * base_rate( rs_result-chargeable_weight ) + * category_factor( iv_category ). + IF iv_dangerous = abap_true. + rs_result-dangerous_fee = rs_result-freight * c_dangerous_rate. + ENDIF. + rs_result-fuel_fee = ( rs_result-freight + rs_result-dangerous_fee ) * c_fuel_rate. + rs_result-security_fee = c_security_fee. + rs_result-total_amount = rs_result-freight + rs_result-dangerous_fee + + rs_result-fuel_fee + rs_result-security_fee. + rs_result-reason = {{p}}if_air_freight=>c_reason-ok. + ENDMETHOD. + + METHOD check_input. + IF iv_weight <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_weight. + ELSEIF iv_volume <= 0. + rv_reason = {{p}}if_air_freight=>c_reason-bad_volume. + ELSEIF iv_category <> {{p}}if_air_freight=>c_category-general + AND iv_category <> {{p}}if_air_freight=>c_category-perishable + AND iv_category <> {{p}}if_air_freight=>c_category-valuable. + rv_reason = {{p}}if_air_freight=>c_reason-bad_category. + ELSE. + rv_reason = {{p}}if_air_freight=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD chargeable_weight. + DATA lv_volumetric TYPE {{p}}if_air_freight=>ty_weight. + DATA lv_gross TYPE {{p}}if_air_freight=>ty_weight. + + lv_volumetric = iv_volume / c_volume_divisor. + lv_gross = nmax( val1 = iv_weight val2 = lv_volumetric ). + rv_weight = ceil( lv_gross ). + ENDMETHOD. + + METHOD base_rate. + IF iv_weight <= 45. + rv_rate = '9.00'. + ELSEIF iv_weight <= 100. + rv_rate = '7.20'. + ELSEIF iv_weight <= 500. + rv_rate = '5.60'. + ELSE. + rv_rate = '4.30'. + ENDIF. + ENDMETHOD. + + METHOD category_factor. + CASE iv_category. + WHEN {{p}}if_air_freight=>c_category-perishable. + rv_factor = '1.25'. + WHEN {{p}}if_air_freight=>c_category-valuable. + rv_factor = '1.50'. + WHEN OTHERS. + rv_factor = '1.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/reference/air_freight_pricer.testclasses.abap b/tasks_gen/train/G1057/reference/air_freight_pricer.testclasses.abap new file mode 100644 index 0000000..b714118 --- /dev/null +++ b/tasks_gen/train/G1057/reference/air_freight_pricer.testclasses.abap @@ -0,0 +1,118 @@ +CLASS ltc_air_freight_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_expected TYPE c LENGTH 20. + + DATA mo_cut TYPE REF TO {{p}}if_air_freight. + + METHODS setup. + METHODS assert_weight + IMPORTING iv_expected TYPE ty_expected + iv_actual TYPE {{p}}if_air_freight=>ty_weight. + METHODS assert_amount + IMPORTING iv_expected TYPE ty_expected + iv_actual TYPE {{p}}if_air_freight=>ty_amount. + + METHODS general_shipment_is_priced FOR TESTING. + METHODS volumetric_weight_wins FOR TESTING. + METHODS dangerous_goods_surcharge FOR TESTING. + METHODS zero_weight_is_rejected FOR TESTING. + METHODS bad_category_is_rejected FOR TESTING. + METHODS too_heavy_is_rejected FOR TESTING. +ENDCLASS. + + +CLASS ltc_air_freight_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}air_freight_pricer( ). + ENDMETHOD. + + METHOD assert_weight. + DATA lv_expected TYPE {{p}}if_air_freight=>ty_weight. + lv_expected = iv_expected. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = iv_actual ). + ENDMETHOD. + + METHOD assert_amount. + DATA lv_expected TYPE {{p}}if_air_freight=>ty_amount. + lv_expected = iv_expected. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = iv_actual ). + ENDMETHOD. + + METHOD general_shipment_is_priced. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '12.000' + iv_volume = '48000.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-ok + act = ls_result-reason ). + assert_weight( iv_expected = '12.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '135.96' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD volumetric_weight_wins. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '5.000' + iv_volume = '120000.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + + assert_weight( iv_expected = '20.000' iv_actual = ls_result-chargeable_weight ). + assert_amount( iv_expected = '216.60' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD dangerous_goods_surcharge. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '10.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-valuable + iv_dangerous = abap_true ). + + assert_amount( iv_expected = '54.00' iv_actual = ls_result-dangerous_fee ). + assert_amount( iv_expected = '226.68' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD zero_weight_is_rejected. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '0.000' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-bad_weight + act = ls_result-reason ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. + + METHOD bad_category_is_rejected. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '10.000' + iv_volume = '600.000' + iv_category = 'EXPRESS' + iv_dangerous = abap_false ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-bad_category + act = ls_result-reason ). + ENDMETHOD. + + METHOD too_heavy_is_rejected. + DATA ls_result TYPE {{p}}if_air_freight=>ty_result. + + ls_result = mo_cut->price( iv_weight = '3000.500' + iv_volume = '600.000' + iv_category = {{p}}if_air_freight=>c_category-general + iv_dangerous = abap_false ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_air_freight=>c_reason-too_heavy + act = ls_result-reason ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total_amount ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1057/seed/if_air_freight.intf.abap b/tasks_gen/train/G1057/seed/if_air_freight.intf.abap new file mode 100644 index 0000000..d03be21 --- /dev/null +++ b/tasks_gen/train/G1057/seed/if_air_freight.intf.abap @@ -0,0 +1,42 @@ +INTERFACE {{p}}if_air_freight PUBLIC. + TYPES: + ty_weight TYPE p LENGTH 9 DECIMALS 3, + ty_volume TYPE p LENGTH 11 DECIMALS 3, + ty_amount TYPE p LENGTH 13 DECIMALS 2, + ty_category TYPE c LENGTH 10, + ty_reason TYPE c LENGTH 12. + + TYPES: + BEGIN OF ty_result, + chargeable_weight TYPE ty_weight, + freight TYPE ty_amount, + dangerous_fee TYPE ty_amount, + fuel_fee TYPE ty_amount, + security_fee TYPE ty_amount, + total_amount TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_category, + general TYPE ty_category VALUE 'GENERAL', + perishable TYPE ty_category VALUE 'PERISHABLE', + valuable TYPE ty_category VALUE 'VALUABLE', + END OF c_category. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + bad_weight TYPE ty_reason VALUE 'BAD_WEIGHT', + bad_volume TYPE ty_reason VALUE 'BAD_VOLUME', + bad_category TYPE ty_reason VALUE 'BAD_CATEGORY', + too_heavy TYPE ty_reason VALUE 'TOO_HEAVY', + END OF c_reason. + + METHODS price + IMPORTING iv_weight TYPE ty_weight + iv_volume TYPE ty_volume + iv_category TYPE ty_category + iv_dangerous TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1057/spec.md b/tasks_gen/train/G1057/spec.md new file mode 100644 index 0000000..7f4a4dd --- /dev/null +++ b/tasks_gen/train/G1057/spec.md @@ -0,0 +1,66 @@ +# 1. Goal +A freight forwarder prices air freight shipments. The sales desk calls one class +for each shipment. The class returns the chargeable weight, the freight, the +surcharges and the total charge of the shipment. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_AIR_FREIGHT exists in package $TMP. It is active. +- The interface defines the input types, the result structure, the goods + categories, the reason codes and one method. Read the interface before you + start. +- The method receives the actual weight in kilograms, the volume in cubic + centimetres, the goods category and a flag that marks a dangerous goods + shipment. +- The result structure has one field for the chargeable weight, one field for + each amount and one field for the reason. + +# 4. Contract +- Create the class {{P}}AIR_FREIGHT_PRICER in package $TMP. +- The class is public, final and has a public constructor without parameters. +- The class implements {{P}}IF_AIR_FREIGHT. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. If the actual weight is not positive, return BAD_WEIGHT. +2. If the volume is not positive, return BAD_VOLUME. +3. If the category is not one of the categories of the interface, return + BAD_CATEGORY. +4. The volumetric weight is the volume divided by 6000. The chargeable weight + is the larger of the actual weight and the volumetric weight, rounded up to + the next whole kilogram. A value that is already a whole number does not + change. +5. If the chargeable weight is more than 3000 kg, return TOO_HEAVY. +6. The base rate per kilogram depends on the chargeable weight: + - up to 45 kg: 9.00 + - more than 45 kg and up to 100 kg: 7.20 + - more than 100 kg and up to 500 kg: 5.60 + - more than 500 kg: 4.30 +7. The category factor is 1.00 for the general category, 1.25 for the + perishable category and 1.50 for the valuable category. +8. The freight is the chargeable weight multiplied by the base rate and by the + category factor. +9. If the shipment is dangerous, the dangerous goods surcharge is 40 per cent + of the freight. Otherwise the dangerous goods surcharge is zero. +10. The fuel surcharge is 12 per cent of the freight plus the dangerous goods + surcharge. +11. The security fee is 15.00. +12. The total is the freight plus the dangerous goods surcharge plus the fuel + surcharge plus the security fee. +13. Round each amount to two decimal places before you use it in the next step. +14. If one of the rules 1 to 5 fails, return the reason and set every numeric + field of the result to zero. If no rule fails, return OK. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- 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_AIR_FREIGHT. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1057/task.json b/tasks_gen/train/G1057/task.json new file mode 100644 index 0000000..7c3b335 --- /dev/null +++ b/tasks_gen/train/G1057/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1057", + "category": "F", + "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_AIR_FREIGHT", + "file": "seed/if_air_freight.intf.abap", + "description": "Air freight pricing contract: input types, result structure, categories, reason codes and method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}AIR_FREIGHT_PRICER", + "implements": "{{P}}IF_AIR_FREIGHT" + } + ], + "out_of_scope": [ + "{{P}}IF_AIR_FREIGHT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}AIR_FRT_HIDDEN", + "file": "hidden/air_frt_hidden.clas.abap", + "description": "Hidden tests for the air freight pricer" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}AIR_FREIGHT_PRICER", + "file": "reference/air_freight_pricer.clas.abap", + "description": "Air freight pricer", + "testclasses_file": "reference/air_freight_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1058/faulty/m0_ice_fee.func.abap b/tasks_gen/train/G1058/faulty/m0_ice_fee.func.abap new file mode 100644 index 0000000..b173260 --- /dev/null +++ b/tasks_gen/train/G1058/faulty/m0_ice_fee.func.abap @@ -0,0 +1,61 @@ +FUNCTION {{p}}ice_fee + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + CONSTANTS lc_vanilla TYPE decfloat34 VALUE '1.20'. + CONSTANTS lc_chocolate TYPE decfloat34 VALUE '1.50'. + CONSTANTS lc_strawberry TYPE decfloat34 VALUE '1.40'. + CONSTANTS lc_nut TYPE decfloat34 VALUE '0.60'. + CONSTANTS lc_sprinkles TYPE decfloat34 VALUE '0.40'. + CONSTANTS lc_service_rate TYPE decfloat34 VALUE '1.05'. + + DATA lv_scoop_price TYPE decfloat34. + DATA lv_topping_price TYPE decfloat34. + DATA lv_total TYPE decfloat34. + DATA lv_price TYPE p LENGTH 8 DECIMALS 2. + + IF iv_scoops >= 1 OR iv_scoops > 5. + RAISE invalid_scoops. + ENDIF. + + CASE iv_flavour. + WHEN 'VA'. + lv_scoop_price = lc_vanilla. + WHEN 'CH'. + lv_scoop_price = lc_chocolate. + WHEN 'ST'. + lv_scoop_price = lc_strawberry. + WHEN OTHERS. + RAISE unknown_flavour. + ENDCASE. + + CASE iv_topping. + WHEN 'NON' OR ''. + CLEAR lv_topping_price. + WHEN 'NUT'. + lv_topping_price = lc_nut. + WHEN 'SPR'. + lv_topping_price = lc_sprinkles. + WHEN OTHERS. + RAISE unknown_topping. + ENDCASE. + + lv_total = iv_scoops * lv_scoop_price + lv_topping_price. + + IF iv_here = abap_true. + lv_total = lv_total * lc_service_rate. + ENDIF. + + lv_price = lv_total. + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/faulty/m2_ice_fee.func.abap b/tasks_gen/train/G1058/faulty/m2_ice_fee.func.abap new file mode 100644 index 0000000..11d1965 --- /dev/null +++ b/tasks_gen/train/G1058/faulty/m2_ice_fee.func.abap @@ -0,0 +1,61 @@ +FUNCTION {{p}}ice_fee + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + CONSTANTS lc_vanilla TYPE decfloat34 VALUE '1.20'. + CONSTANTS lc_chocolate TYPE decfloat34 VALUE '1.50'. + CONSTANTS lc_strawberry TYPE decfloat34 VALUE '1.40'. + CONSTANTS lc_nut TYPE decfloat34 VALUE '0.60'. + CONSTANTS lc_sprinkles TYPE decfloat34 VALUE '0.40'. + CONSTANTS lc_service_rate TYPE decfloat34 VALUE '1.05'. + + DATA lv_scoop_price TYPE decfloat34. + DATA lv_topping_price TYPE decfloat34. + DATA lv_total TYPE decfloat34. + DATA lv_price TYPE p LENGTH 8 DECIMALS 2. + + IF iv_scoops < 1 OR iv_scoops > 5. + RAISE invalid_scoops. + ENDIF. + + CASE iv_flavour. + WHEN 'VA'. + lv_scoop_price = lc_vanilla. + WHEN 'CH'. + lv_scoop_price = lc_chocolate. + WHEN 'ST'. + lv_scoop_price = lc_strawberry. + WHEN OTHERS. + RAISE unknown_flavour. + ENDCASE. + + CASE iv_topping. + WHEN 'NON' OR ''. + CLEAR lv_topping_price. + WHEN 'NUT'. + lv_topping_price = lc_nut. + WHEN 'SPR'. + lv_topping_price = lc_sprinkles. + WHEN OTHERS. + RAISE unknown_topping. + ENDCASE. + + lv_total = iv_scoops / lv_scoop_price + lv_topping_price. + + IF iv_here = abap_true. + lv_total = lv_total * lc_service_rate. + ENDIF. + + lv_price = lv_total. + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/faulty/m3_ice_fee.func.abap b/tasks_gen/train/G1058/faulty/m3_ice_fee.func.abap new file mode 100644 index 0000000..05a1ac9 --- /dev/null +++ b/tasks_gen/train/G1058/faulty/m3_ice_fee.func.abap @@ -0,0 +1,61 @@ +FUNCTION {{p}}ice_fee + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + CONSTANTS lc_vanilla TYPE decfloat34 VALUE '1.20'. + CONSTANTS lc_chocolate TYPE decfloat34 VALUE '1.50'. + CONSTANTS lc_strawberry TYPE decfloat34 VALUE '1.40'. + CONSTANTS lc_nut TYPE decfloat34 VALUE '0.60'. + CONSTANTS lc_sprinkles TYPE decfloat34 VALUE '0.40'. + CONSTANTS lc_service_rate TYPE decfloat34 VALUE '1.05'. + + DATA lv_scoop_price TYPE decfloat34. + DATA lv_topping_price TYPE decfloat34. + DATA lv_total TYPE decfloat34. + DATA lv_price TYPE p LENGTH 8 DECIMALS 2. + + IF iv_scoops < 1 OR iv_scoops > 5. + RAISE invalid_scoops. + ENDIF. + + CASE iv_flavour. + WHEN 'VA'. + lv_scoop_price = lc_vanilla. + WHEN 'CH'. + lv_scoop_price = lc_chocolate. + WHEN 'ST'. + lv_scoop_price = lc_strawberry. + WHEN OTHERS. + RAISE unknown_flavour. + ENDCASE. + + CASE iv_topping. + WHEN 'NON' OR ''. + CLEAR lv_topping_price. + WHEN 'NUT'. + lv_topping_price = lc_nut. + WHEN 'SPR'. + lv_topping_price = lc_sprinkles. + WHEN OTHERS. + RAISE unknown_topping. + ENDCASE. + + lv_total = iv_scoops * lv_scoop_price + lv_topping_price. + + IF iv_here = abap_false. + lv_total = lv_total * lc_service_rate. + ENDIF. + + lv_price = lv_total. + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/faulty/m4_ice_fee.func.abap b/tasks_gen/train/G1058/faulty/m4_ice_fee.func.abap new file mode 100644 index 0000000..195ad13 --- /dev/null +++ b/tasks_gen/train/G1058/faulty/m4_ice_fee.func.abap @@ -0,0 +1,61 @@ +FUNCTION {{p}}ice_fee + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + CONSTANTS lc_vanilla TYPE decfloat34 VALUE '1.20'. + CONSTANTS lc_chocolate TYPE decfloat34 VALUE '1.50'. + CONSTANTS lc_strawberry TYPE decfloat34 VALUE '1.40'. + CONSTANTS lc_nut TYPE decfloat34 VALUE '0.60'. + CONSTANTS lc_sprinkles TYPE decfloat34 VALUE '0.40'. + CONSTANTS lc_service_rate TYPE decfloat34 VALUE '1.05'. + + DATA lv_scoop_price TYPE decfloat34. + DATA lv_topping_price TYPE decfloat34. + DATA lv_total TYPE decfloat34. + DATA lv_price TYPE p LENGTH 8 DECIMALS 2. + + IF iv_scoops < 1 OR iv_scoops > 5. + RAISE invalid_scoops. + ENDIF. + + CASE iv_flavour. + WHEN 'VA'. + lv_scoop_price = lc_vanilla. + WHEN 'CH'. + lv_scoop_price = lc_chocolate. + WHEN 'ST'. + lv_scoop_price = lc_strawberry. + WHEN OTHERS. + RAISE unknown_flavour. + ENDCASE. + + CASE iv_topping. + WHEN 'NON' OR ''. + CLEAR lv_topping_price. + WHEN 'NUT'. + lv_topping_price = lc_nut. + WHEN 'SPR'. + lv_topping_price = lc_sprinkles. + WHEN OTHERS. + RAISE unknown_topping. + ENDCASE. + + lv_total = iv_scoops * lv_scoop_price + lv_topping_price. + + IF iv_here = abap_true. + lv_total = lv_total / lc_service_rate. + ENDIF. + + lv_price = lv_total. + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/generation.json b/tasks_gen/train/G1058/generation.json new file mode 100644 index 0000000..731a847 --- /dev/null +++ b/tasks_gen/train/G1058/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1058", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1058/hidden/t37_hidden.clas.abap b/tasks_gen/train/G1058/hidden/t37_hidden.clas.abap new file mode 100644 index 0000000..4ed73a1 --- /dev/null +++ b/tasks_gen/train/G1058/hidden/t37_hidden.clas.abap @@ -0,0 +1,127 @@ +CLASS {{p}}t37_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS one_scoop_vanilla FOR TESTING. + METHODS three_scoops_chocolate FOR TESTING. + METHODS five_scoops_strawberry FOR TESTING. + METHODS topping_nut FOR TESTING. + METHODS topping_sprinkles FOR TESTING. + METHODS empty_topping FOR TESTING. + METHODS service_charge FOR TESTING. + METHODS service_rounding FOR TESTING. + METHODS rejects_zero_scoops FOR TESTING. + METHODS rejects_six_scoops FOR TESTING. + METHODS rejects_unknown_flavour FOR TESTING. + METHODS rejects_unknown_topping FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t37_hidden IMPLEMENTATION. + METHOD call. + DATA lv_price TYPE decfloat34. + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = iv_flavour + iv_scoops = iv_scoops + iv_topping = iv_topping + iv_here = iv_here + IMPORTING + ev_price = lv_price + EXCEPTIONS + invalid_scoops = 1 + unknown_flavour = 2 + unknown_topping = 3 + OTHERS = 4. + ev_subrc = sy-subrc. + ev_price = lv_price. + ENDMETHOD. + + METHOD one_scoop_vanilla. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.20' ) act = lv_price ). + ENDMETHOD. + + METHOD three_scoops_chocolate. + call( EXPORTING iv_flavour = 'CH' iv_scoops = 3 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.50' ) act = lv_price ). + ENDMETHOD. + + METHOD five_scoops_strawberry. + call( EXPORTING iv_flavour = 'ST' iv_scoops = 5 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '7.00' ) act = lv_price ). + ENDMETHOD. + + METHOD topping_nut. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 2 iv_topping = 'NUT' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.00' ) act = lv_price ). + ENDMETHOD. + + METHOD topping_sprinkles. + call( EXPORTING iv_flavour = 'ST' iv_scoops = 1 iv_topping = 'SPR' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.80' ) act = lv_price ). + ENDMETHOD. + + METHOD empty_topping. + call( EXPORTING iv_flavour = 'CH' iv_scoops = 2 iv_topping = '' iv_here = abap_false + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.00' ) act = lv_price ). + ENDMETHOD. + + METHOD service_charge. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_true + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.26' ) act = lv_price ). + ENDMETHOD. + + METHOD service_rounding. + call( EXPORTING iv_flavour = 'CH' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_true + IMPORTING ev_price = DATA(lv_price) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.58' ) act = lv_price ). + ENDMETHOD. + + METHOD rejects_zero_scoops. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 0 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD rejects_six_scoops. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 6 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD rejects_unknown_flavour. + call( EXPORTING iv_flavour = 'XX' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD rejects_unknown_topping. + call( EXPORTING iv_flavour = 'VA' iv_scoops = 1 iv_topping = 'ZZ' iv_here = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1058/mutation.json b/tasks_gen/train/G1058/mutation.json new file mode 100644 index 0000000..b53ae8d --- /dev/null +++ b/tasks_gen/train/G1058/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1058", + "mutants": [ + { + "object": "{{P}}ICE_FEE", + "mutant": "line 26: < -> >= (rel)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "EMPTY_TOPPING", + "FIVE_SCOOPS_STRAWBERRY", + "ONE_SCOOP_VANILLA", + "REJECTS_UNKNOWN_FLAVOUR", + "REJECTS_UNKNOWN_TOPPING", + "REJECTS_ZERO_SCOOPS", + "SERVICE_CHARGE", + "SERVICE_ROUNDING", + "THREE_SCOOPS_CHOCOLATE", + "TOPPING_NUT", + "TOPPING_SPRINKLES" + ] + }, + { + "object": "{{P}}ICE_FEE", + "mutant": "line 42: OR -> AND (logic)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}ICE_FEE", + "mutant": "line 52: * -> / (arith)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "EMPTY_TOPPING", + "FIVE_SCOOPS_STRAWBERRY", + "ONE_SCOOP_VANILLA", + "SERVICE_CHARGE", + "SERVICE_ROUNDING", + "THREE_SCOOPS_CHOCOLATE", + "TOPPING_NUT", + "TOPPING_SPRINKLES" + ] + }, + { + "object": "{{P}}ICE_FEE", + "mutant": "line 54: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "EMPTY_TOPPING", + "FIVE_SCOOPS_STRAWBERRY", + "ONE_SCOOP_VANILLA", + "SERVICE_CHARGE", + "SERVICE_ROUNDING", + "THREE_SCOOPS_CHOCOLATE", + "TOPPING_NUT", + "TOPPING_SPRINKLES" + ] + }, + { + "object": "{{P}}ICE_FEE", + "mutant": "line 55: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "SERVICE_CHARGE", + "SERVICE_ROUNDING" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1058/reference/ice_fee.func.abap b/tasks_gen/train/G1058/reference/ice_fee.func.abap new file mode 100644 index 0000000..88d7e0b --- /dev/null +++ b/tasks_gen/train/G1058/reference/ice_fee.func.abap @@ -0,0 +1,61 @@ +FUNCTION {{p}}ice_fee + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + CONSTANTS lc_vanilla TYPE decfloat34 VALUE '1.20'. + CONSTANTS lc_chocolate TYPE decfloat34 VALUE '1.50'. + CONSTANTS lc_strawberry TYPE decfloat34 VALUE '1.40'. + CONSTANTS lc_nut TYPE decfloat34 VALUE '0.60'. + CONSTANTS lc_sprinkles TYPE decfloat34 VALUE '0.40'. + CONSTANTS lc_service_rate TYPE decfloat34 VALUE '1.05'. + + DATA lv_scoop_price TYPE decfloat34. + DATA lv_topping_price TYPE decfloat34. + DATA lv_total TYPE decfloat34. + DATA lv_price TYPE p LENGTH 8 DECIMALS 2. + + IF iv_scoops < 1 OR iv_scoops > 5. + RAISE invalid_scoops. + ENDIF. + + CASE iv_flavour. + WHEN 'VA'. + lv_scoop_price = lc_vanilla. + WHEN 'CH'. + lv_scoop_price = lc_chocolate. + WHEN 'ST'. + lv_scoop_price = lc_strawberry. + WHEN OTHERS. + RAISE unknown_flavour. + ENDCASE. + + CASE iv_topping. + WHEN 'NON' OR ''. + CLEAR lv_topping_price. + WHEN 'NUT'. + lv_topping_price = lc_nut. + WHEN 'SPR'. + lv_topping_price = lc_sprinkles. + WHEN OTHERS. + RAISE unknown_topping. + ENDCASE. + + lv_total = iv_scoops * lv_scoop_price + lv_topping_price. + + IF iv_here = abap_true. + lv_total = lv_total * lc_service_rate. + ENDIF. + + lv_price = lv_total. + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/reference/t37_test.clas.abap b/tasks_gen/train/G1058/reference/t37_test.clas.abap new file mode 100644 index 0000000..6a88434 --- /dev/null +++ b/tasks_gen/train/G1058/reference/t37_test.clas.abap @@ -0,0 +1,135 @@ +CLASS {{p}}ice_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_of + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE decfloat34. + METHODS one_scoop_vanilla FOR TESTING. + METHODS three_scoops_chocolate FOR TESTING. + METHODS topping_prices FOR TESTING. + METHODS service_charge FOR TESTING. + METHODS rejects_bad_scoops FOR TESTING. + METHODS rejects_unknown_codes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ice_test IMPLEMENTATION. + METHOD price_of. + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = iv_flavour + iv_scoops = iv_scoops + iv_topping = iv_topping + iv_here = iv_here + IMPORTING + ev_price = rv_price + EXCEPTIONS + OTHERS = 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD one_scoop_vanilla. + CONSTANTS lc_exp TYPE decfloat34 VALUE '1.20'. + cl_abap_unit_assert=>assert_equals( + exp = lc_exp + act = price_of( iv_flavour = 'VA' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_false ) ). + ENDMETHOD. + + METHOD three_scoops_chocolate. + CONSTANTS lc_exp TYPE decfloat34 VALUE '4.50'. + cl_abap_unit_assert=>assert_equals( + exp = lc_exp + act = price_of( iv_flavour = 'CH' iv_scoops = 3 iv_topping = 'NON' iv_here = abap_false ) ). + ENDMETHOD. + + METHOD topping_prices. + CONSTANTS lc_nut_exp TYPE decfloat34 VALUE '3.00'. + CONSTANTS lc_spr_exp TYPE decfloat34 VALUE '1.80'. + cl_abap_unit_assert=>assert_equals( + exp = lc_nut_exp + act = price_of( iv_flavour = 'VA' iv_scoops = 2 iv_topping = 'NUT' iv_here = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = lc_spr_exp + act = price_of( iv_flavour = 'ST' iv_scoops = 1 iv_topping = 'SPR' iv_here = abap_false ) ). + ENDMETHOD. + + METHOD service_charge. + CONSTANTS lc_plain_exp TYPE decfloat34 VALUE '1.26'. + CONSTANTS lc_round_exp TYPE decfloat34 VALUE '1.58'. + cl_abap_unit_assert=>assert_equals( + exp = lc_plain_exp + act = price_of( iv_flavour = 'VA' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = lc_round_exp + act = price_of( iv_flavour = 'CH' iv_scoops = 1 iv_topping = 'NON' iv_here = abap_true ) ). + ENDMETHOD. + + METHOD rejects_bad_scoops. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = 'VA' + iv_scoops = 0 + iv_topping = 'NON' + iv_here = abap_false + IMPORTING + ev_price = lv_price + EXCEPTIONS + invalid_scoops = 1 + OTHERS = 2. + lv_rc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = 'VA' + iv_scoops = 6 + iv_topping = 'NON' + iv_here = abap_false + IMPORTING + ev_price = lv_price + EXCEPTIONS + invalid_scoops = 1 + OTHERS = 2. + lv_rc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD rejects_unknown_codes. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = 'XX' + iv_scoops = 1 + iv_topping = 'NON' + iv_here = abap_false + IMPORTING + ev_price = lv_price + EXCEPTIONS + unknown_flavour = 1 + OTHERS = 2. + lv_rc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + + CALL FUNCTION '{{P}}ICE_FEE' + EXPORTING + iv_flavour = 'VA' + iv_scoops = 1 + iv_topping = 'ZZ' + iv_here = abap_false + IMPORTING + ev_price = lv_price + EXCEPTIONS + unknown_topping = 1 + OTHERS = 2. + lv_rc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1058/seed/ice_sample.func.abap b/tasks_gen/train/G1058/seed/ice_sample.func.abap new file mode 100644 index 0000000..c90391c --- /dev/null +++ b/tasks_gen/train/G1058/seed/ice_sample.func.abap @@ -0,0 +1,18 @@ +FUNCTION {{p}}ice_sample + IMPORTING + iv_flavour TYPE string + iv_scoops TYPE i + iv_topping TYPE string + iv_here TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_scoops + unknown_flavour + unknown_topping. + + " Sample module of the group. It only shows the interface of the group. + " The logic of this sample module is not part of this task. + ev_price = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1058/spec.md b/tasks_gen/train/G1058/spec.md new file mode 100644 index 0000000..b6bd73b --- /dev/null +++ b/tasks_gen/train/G1058/spec.md @@ -0,0 +1,55 @@ +# 1. Goal +An ice cream parlour sells ice cream by the scoop. The checkout program calls +one function module for each order. The function module calculates the price of +the order. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_ICE is in package $TMP. +- Function module {{P}}ICE_SAMPLE is in this group. It is a sample module. Its + interface is the interface that all modules of this group use. + +# 4. Contract +- Create the function module {{P}}ICE_FEE in function group {{P}}FG_ICE. +- Use exactly the interface of {{P}}ICE_SAMPLE: + - iv_flavour, importing, type string. The flavour code. + - iv_scoops, importing, type i. The number of scoops. + - iv_topping, importing, type string. The topping code. + - iv_here, importing, type abap_bool. True if the customer eats the ice cream + in the parlour. + - ev_price, exporting, type decfloat34. The price of the order. + - Exceptions: invalid_scoops, unknown_flavour, unknown_topping. + +# 5. Business rules +1. The number of scoops must be between 1 and 5. If it is not, raise the + exception INVALID_SCOOPS. +2. The flavour code gives the price of one scoop: + - VA: 1.20 + - CH: 1.50 + - ST: 1.40 + Any other flavour code raises the exception UNKNOWN_FLAVOUR. +3. The topping code gives the price of the topping: + - NON or empty: 0.00 + - NUT: 0.60 + - SPR: 0.40 + Any other topping code raises the exception UNKNOWN_TOPPING. +4. The base price is the number of scoops multiplied by the price of one scoop, + plus the price of the topping. +5. If iv_here is true, add a service charge of 5 percent of the base price. If + iv_here is false, there is no service charge. +6. Round the final price half up to 2 decimals. Return the result in ev_price. + +# 6. Constraints +- Release target: 7.40 SP05. Use only ABAP syntax that exists in 7.40 SP05. Do + not use syntax that was introduced later. For example, do not use common + table expressions (WITH), UNION, INTERSECT or EXCEPT in Open SQL, IS INSTANCE + OF, or CASE TYPE OF. +- Coding standard: Clean ABAP. +- Out of scope: do not change {{P}}ICE_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1058/task.json b/tasks_gen/train/G1058/task.json new file mode 100644 index 0000000..cc6e902 --- /dev/null +++ b/tasks_gen/train/G1058/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1058", + "category": "G", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v740sp05", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_ICE", + "description": "Ice cream pricing" + }, + { + "type": "FUNC", + "name": "{{P}}ICE_SAMPLE", + "functionGroup": "{{P}}FG_ICE", + "file": "seed/ice_sample.func.abap", + "description": "Sample module that shows the interface of the group" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}ICE_FEE", + "functionGroup": "{{P}}FG_ICE", + "params": [ + { + "name": "iv_flavour", + "type": "string" + }, + { + "name": "iv_scoops", + "type": "i" + }, + { + "name": "iv_topping", + "type": "string" + }, + { + "name": "iv_here", + "type": "abap_bool" + }, + { + "name": "ev_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}ICE_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T37_HIDDEN", + "file": "hidden/t37_hidden.clas.abap", + "description": "T37 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}ICE_FEE", + "functionGroup": "{{P}}FG_ICE", + "file": "reference/ice_fee.func.abap", + "description": "Ice cream price module" + }, + { + "type": "CLAS", + "name": "{{P}}ICE_TEST", + "file": "reference/t37_test.clas.abap", + "description": "T37 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1059/generation.json b/tasks_gen/train/G1059/generation.json new file mode 100644 index 0000000..cee3435 --- /dev/null +++ b/tasks_gen/train/G1059/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1059", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1059/reference/sauna_pricer.clas.abap b/tasks_gen/train/G1059/reference/sauna_pricer.clas.abap new file mode 100644 index 0000000..ce9e50d --- /dev/null +++ b/tasks_gen/train/G1059/reference/sauna_pricer.clas.abap @@ -0,0 +1,43 @@ +CLASS {{p}}sauna_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_sauna_price. + + PRIVATE SECTION. + CONSTANTS: + c_min_days TYPE i VALUE 1, + c_max_days TYPE i VALUE 14, + c_min_guests TYPE i VALUE 1, + c_max_guests TYPE i VALUE 10, + c_many_guests TYPE i VALUE 6, + c_rate_base TYPE {{p}}if_sauna_price=>ty_amount VALUE '120.00', + c_rate_weekend TYPE {{p}}if_sauna_price=>ty_amount VALUE '150.00', + c_rate_group TYPE {{p}}if_sauna_price=>ty_amount VALUE '140.00'. + + METHODS rate_per_day + IMPORTING iv_guests TYPE i + iv_weekend TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE {{p}}if_sauna_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}sauna_pricer IMPLEMENTATION. + METHOD {{p}}if_sauna_price~calculate. + IF iv_days < c_min_days OR iv_days > c_max_days + OR iv_guests < c_min_guests OR iv_guests > c_max_guests. + RETURN. + ENDIF. + rv_price = rate_per_day( iv_guests = iv_guests + iv_weekend = iv_weekend ) * iv_days. + ENDMETHOD. + + METHOD rate_per_day. + " Answer to the open question: the weekend rate has priority. + IF iv_weekend = abap_true. + rv_rate = c_rate_weekend. + ELSEIF iv_guests > c_many_guests. + rv_rate = c_rate_group. + ELSE. + rv_rate = c_rate_base. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1059/reference/sauna_pricer.testclasses.abap b/tasks_gen/train/G1059/reference/sauna_pricer.testclasses.abap new file mode 100644 index 0000000..5106ed2 --- /dev/null +++ b/tasks_gen/train/G1059/reference/sauna_pricer.testclasses.abap @@ -0,0 +1,54 @@ +CLASS ltc_sauna_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_sauna_price. + METHODS setup. + METHODS base_hire FOR TESTING. + METHODS weekend_hire FOR TESTING. + METHODS group_hire FOR TESTING. + METHODS weekend_group_hire FOR TESTING. + METHODS too_many_days FOR TESTING. + METHODS no_guests FOR TESTING. +ENDCLASS. + + +CLASS ltc_sauna_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}sauna_pricer( ). + ENDMETHOD. + + METHOD base_hire. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '360.00' ) + act = mo_cut->calculate( iv_days = 3 iv_guests = 4 iv_weekend = abap_false ) ). + ENDMETHOD. + + METHOD weekend_hire. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '300.00' ) + act = mo_cut->calculate( iv_days = 2 iv_guests = 4 iv_weekend = abap_true ) ). + ENDMETHOD. + + METHOD group_hire. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '420.00' ) + act = mo_cut->calculate( iv_days = 3 iv_guests = 8 iv_weekend = abap_false ) ). + ENDMETHOD. + + METHOD weekend_group_hire. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '300.00' ) + act = mo_cut->calculate( iv_days = 2 iv_guests = 8 iv_weekend = abap_true ) ). + ENDMETHOD. + + METHOD too_many_days. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '0.00' ) + act = mo_cut->calculate( iv_days = 15 iv_guests = 4 iv_weekend = abap_false ) ). + ENDMETHOD. + + METHOD no_guests. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_sauna_price=>ty_amount( '0.00' ) + act = mo_cut->calculate( iv_days = 2 iv_guests = 0 iv_weekend = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1059/seed/if_sauna_price.intf.abap b/tasks_gen/train/G1059/seed/if_sauna_price.intf.abap new file mode 100644 index 0000000..4c84ee7 --- /dev/null +++ b/tasks_gen/train/G1059/seed/if_sauna_price.intf.abap @@ -0,0 +1,8 @@ +INTERFACE {{p}}if_sauna_price PUBLIC. + TYPES ty_amount TYPE p LENGTH 11 DECIMALS 2. + METHODS calculate + IMPORTING iv_days TYPE i + iv_guests TYPE i + iv_weekend TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDINTERFACE. diff --git a/tasks_gen/train/G1059/spec.md b/tasks_gen/train/G1059/spec.md new file mode 100644 index 0000000..8a505d7 --- /dev/null +++ b/tasks_gen/train/G1059/spec.md @@ -0,0 +1,42 @@ +# 1. Goal +Calculate the price of a hire of a mobile sauna trailer. A booking program calls +the calculation before it confirms a booking. The price depends on the number of +days, the number of guests and the day of the week. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_SAUNA_PRICE exists in package $TMP. It is active. +- The interface has the type TY_AMOUNT (P length 11, 2 decimals) and one method: + CALCULATE IMPORTING iv_days TYPE i, iv_guests TYPE i, iv_weekend TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. +- iv_weekend is abap_true when the hire starts on a Saturday or on a Sunday. + +# 4. Contract +- Create the class {{P}}SAUNA_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_SAUNA_PRICE. +- Do not add other public methods. + +# 5. Business rules +1. A hire has between 1 and 14 days. If iv_days is less than 1 or more than 14, + return 0. +2. A hire has between 1 and 10 guests. If iv_guests is less than 1 or more than + 10, return 0. +3. The price of a hire is 120.00 EUR for each day. +4. A weekend hire costs 150.00 EUR for each day. +5. A hire with more than 6 guests costs 140.00 EUR for each day. +6. The price of the hire is the price for each day multiplied by the number of + days. + +# 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_SAUNA_PRICE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The price of each valid hire is correct. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1059/task.json b/tasks_gen/train/G1059/task.json new file mode 100644 index 0000000..5b5f77b --- /dev/null +++ b/tasks_gen/train/G1059/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1059", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "Rules 4 and 5 give two different prices for each day (150.00 EUR and 140.00 EUR) for a hire that is both a weekend hire and has more than 6 guests, and the spec does not say which rule wins.", + "keywords": [ + [ + "weekend", + "guests" + ], + [ + "weekend", + "price" + ], + [ + "guests", + "price" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_SAUNA_PRICE", + "file": "seed/if_sauna_price.intf.abap", + "description": "Sauna hire price contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SAUNA_PRICER", + "implements": "{{P}}IF_SAUNA_PRICE" + } + ], + "out_of_scope": [ + "{{P}}IF_SAUNA_PRICE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SAUNA_PRICER", + "file": "reference/sauna_pricer.clas.abap", + "description": "Sauna hire price calculation", + "testclasses_file": "reference/sauna_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1060/faulty/m0_i_air_quality.ddls.asddls b/tasks_gen/train/G1060/faulty/m0_i_air_quality.ddls.asddls new file mode 100644 index 0000000..13f931b --- /dev/null +++ b/tasks_gen/train/G1060/faulty/m0_i_air_quality.ddls.asddls @@ -0,0 +1,23 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Air quality per station' +define view entity {{P}}I_AIR_QUALITY + as select from {{p}}station as sta + left outer join {{p}}reading as rea on rea.station_id = sta.station_id +{ + key sta.station_id as station_id, + sta.station_name as station_name, + count( * ) as reading_count, + avg( rea.pm_value as abap.dec( 5, 1 ) ) as avg_pm, + max( rea.pm_value ) as max_pm, + case when max( rea.pm_value ) > sta.limit_pm + then 'HIGH' + when max( rea.pm_value ) > sta.limit_pm * 80 / 100 + then 'MEDIUM' + else 'LOW' + end as rating, + abs( max( rea.pm_value ) - sta.limit_pm ) as deviation +} +group by + sta.station_id, + sta.station_name, + sta.limit_pm diff --git a/tasks_gen/train/G1060/faulty/m3_i_air_quality.ddls.asddls b/tasks_gen/train/G1060/faulty/m3_i_air_quality.ddls.asddls new file mode 100644 index 0000000..6b472af --- /dev/null +++ b/tasks_gen/train/G1060/faulty/m3_i_air_quality.ddls.asddls @@ -0,0 +1,23 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Air quality per station' +define view entity {{P}}I_AIR_QUALITY + as select from {{p}}station as sta + inner join {{p}}reading as rea on rea.station_id = sta.station_id +{ + key sta.station_id as station_id, + sta.station_name as station_name, + count( * ) as reading_count, + avg( rea.pm_value as abap.dec( 5, 1 ) ) as avg_pm, + max( rea.pm_value ) as max_pm, + case when max( rea.pm_value ) > sta.limit_pm + then 'HIGH' + when max( rea.pm_value ) > sta.limit_pm * 80 / 100 + then 'ZEDIUM' + else 'LOW' + end as rating, + abs( max( rea.pm_value ) - sta.limit_pm ) as deviation +} +group by + sta.station_id, + sta.station_name, + sta.limit_pm diff --git a/tasks_gen/train/G1060/faulty/m4_i_air_quality.ddls.asddls b/tasks_gen/train/G1060/faulty/m4_i_air_quality.ddls.asddls new file mode 100644 index 0000000..13b68b2 --- /dev/null +++ b/tasks_gen/train/G1060/faulty/m4_i_air_quality.ddls.asddls @@ -0,0 +1,23 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Air quality per station' +define view entity {{P}}I_AIR_QUALITY + as select from {{p}}station as sta + inner join {{p}}reading as rea on rea.station_id = sta.station_id +{ + key sta.station_id as station_id, + sta.station_name as station_name, + count( * ) as reading_count, + avg( rea.pm_value as abap.dec( 5, 1 ) ) as avg_pm, + max( rea.pm_value ) as max_pm, + case when max( rea.pm_value ) > sta.limit_pm + then 'HIGH' + when max( rea.pm_value ) > sta.limit_pm * 80 / 100 + then 'MEDIUM' + else 'LOW' + end as rating, + abs( max( rea.pm_value ) + sta.limit_pm ) as deviation +} +group by + sta.station_id, + sta.station_name, + sta.limit_pm diff --git a/tasks_gen/train/G1060/generation.json b/tasks_gen/train/G1060/generation.json new file mode 100644 index 0000000..3b35a56 --- /dev/null +++ b/tasks_gen/train/G1060/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1060", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t41_test.clas.abap line 32: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"go_env\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1060/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1060/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..5686295 --- /dev/null +++ b/tasks_gen/train/G1060/hidden/t41_hidden.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS row_count FOR TESTING. + METHODS reading_counts FOR TESTING. + METHODS avg_pm_value FOR TESTING. + METHODS max_pm_value FOR TESTING. + METHODS rating_low FOR TESTING. + METHODS rating_medium FOR TESTING. + METHODS rating_high FOR TESTING. + METHODS deviation_above FOR TESTING. + METHODS deviation_below FOR TESTING. + METHODS station_name FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_AIR_QUALITY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_sta TYPE STANDARD TABLE OF {{p}}station WITH EMPTY KEY. + DATA lt_rea TYPE STANDARD TABLE OF {{p}}reading WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_sta = VALUE #( ( station_id = 'S1' station_name = 'NORTH' limit_pm = '40.0' ) + ( station_id = 'S2' station_name = 'SOUTH' limit_pm = '25.0' ) + ( station_id = 'S3' station_name = 'EAST' limit_pm = '30.0' ) + ( station_id = 'S4' station_name = 'WEST' limit_pm = '20.0' ) ). + lt_rea = VALUE #( ( station_id = 'S1' reading_no = 1 pm_value = '12.0' ) + ( station_id = 'S1' reading_no = 2 pm_value = '13.0' ) + ( station_id = 'S1' reading_no = 3 pm_value = '15.0' ) + ( station_id = 'S2' reading_no = 1 pm_value = '20.0' ) + ( station_id = 'S2' reading_no = 2 pm_value = '30.0' ) + ( station_id = 'S3' reading_no = 1 pm_value = '26.0' ) + ( station_id = 'S3' reading_no = 2 pm_value = '24.0' ) + ( station_id = 'S3' reading_no = 3 pm_value = '26.0' ) ). + go_env->insert_test_data( lt_sta ). + go_env->insert_test_data( lt_rea ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_air_quality INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. + + METHOD reading_counts. + SELECT SINGLE reading_count FROM {{p}}i_air_quality + WHERE station_id = 'S1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD avg_pm_value. + SELECT SINGLE avg_pm FROM {{p}}i_air_quality + WHERE station_id = 'S1' INTO @DATA(lv_avg). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '13.3' ) + act = CONV decfloat34( lv_avg ) ). + ENDMETHOD. + + METHOD max_pm_value. + SELECT SINGLE max_pm FROM {{p}}i_air_quality + WHERE station_id = 'S3' INTO @DATA(lv_max). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '26.0' ) + act = CONV decfloat34( lv_max ) ). + ENDMETHOD. + + METHOD rating_low. + SELECT SINGLE rating FROM {{p}}i_air_quality + WHERE station_id = 'S1' INTO @DATA(lv_rating). + cl_abap_unit_assert=>assert_equals( exp = 'LOW' act = lv_rating ). + ENDMETHOD. + + METHOD rating_medium. + SELECT SINGLE rating FROM {{p}}i_air_quality + WHERE station_id = 'S3' INTO @DATA(lv_rating). + cl_abap_unit_assert=>assert_equals( exp = 'MEDIUM' act = lv_rating ). + ENDMETHOD. + + METHOD rating_high. + SELECT SINGLE rating FROM {{p}}i_air_quality + WHERE station_id = 'S2' INTO @DATA(lv_rating). + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = lv_rating ). + ENDMETHOD. + + METHOD deviation_above. + SELECT SINGLE deviation FROM {{p}}i_air_quality + WHERE station_id = 'S2' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.0' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD deviation_below. + SELECT SINGLE deviation FROM {{p}}i_air_quality + WHERE station_id = 'S1' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.0' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD station_name. + SELECT SINGLE station_name FROM {{p}}i_air_quality + WHERE station_id = 'S3' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'EAST' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1060/mutation.json b/tasks_gen/train/G1060/mutation.json new file mode 100644 index 0000000..097e60d --- /dev/null +++ b/tasks_gen/train/G1060/mutation.json @@ -0,0 +1,51 @@ +{ + "task": "G1060", + "mutants": [ + { + "object": "{{P}}I_AIR_QUALITY", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "ROW_COUNT" + ] + }, + { + "object": "{{P}}I_AIR_QUALITY", + "mutant": "line 10: avg( -> max( (agg)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}I_AIR_QUALITY", + "mutant": "line 14: 80 -> 81 (const)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + }, + { + "object": "{{P}}I_AIR_QUALITY", + "mutant": "line 15: 'MEDIUM' -> 'ZEDIUM' (lit)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "RATING_MEDIUM" + ] + }, + { + "object": "{{P}}I_AIR_QUALITY", + "mutant": "line 18: - -> + (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "DEVIATION_ABOVE", + "DEVIATION_BELOW" + ] + } + ], + "valid": 4, + "killed": 3, + "kill_rate": 0.75, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1060/reference/i_air_quality.ddls.asddls b/tasks_gen/train/G1060/reference/i_air_quality.ddls.asddls new file mode 100644 index 0000000..498717a --- /dev/null +++ b/tasks_gen/train/G1060/reference/i_air_quality.ddls.asddls @@ -0,0 +1,23 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Air quality per station' +define view entity {{P}}I_AIR_QUALITY + as select from {{p}}station as sta + inner join {{p}}reading as rea on rea.station_id = sta.station_id +{ + key sta.station_id as station_id, + sta.station_name as station_name, + count( * ) as reading_count, + avg( rea.pm_value as abap.dec( 5, 1 ) ) as avg_pm, + max( rea.pm_value ) as max_pm, + case when max( rea.pm_value ) > sta.limit_pm + then 'HIGH' + when max( rea.pm_value ) > sta.limit_pm * 80 / 100 + then 'MEDIUM' + else 'LOW' + end as rating, + abs( max( rea.pm_value ) - sta.limit_pm ) as deviation +} +group by + sta.station_id, + sta.station_name, + sta.limit_pm diff --git a/tasks_gen/train/G1060/reference/t41_test.clas.abap b/tasks_gen/train/G1060/reference/t41_test.clas.abap new file mode 100644 index 0000000..6d31ebd --- /dev/null +++ b/tasks_gen/train/G1060/reference/t41_test.clas.abap @@ -0,0 +1,41 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS rating_and_average FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_AIR_QUALITY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD rating_and_average. + DATA lt_sta TYPE STANDARD TABLE OF {{p}}station WITH EMPTY KEY. + DATA lt_rea TYPE STANDARD TABLE OF {{p}}reading WITH EMPTY KEY. + lt_sta = VALUE #( ( station_id = 'A' station_name = 'ALPHA' limit_pm = '50.0' ) ). + lt_rea = VALUE #( ( station_id = 'A' reading_no = 1 pm_value = '10.0' ) + ( station_id = 'A' reading_no = 2 pm_value = '20.0' ) ). + go_env->insert_test_data( lt_sta ). + go_env->insert_test_data( lt_rea ). + SELECT SINGLE rating, avg_pm, deviation FROM {{p}}i_air_quality + WHERE station_id = 'A' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 'LOW' act = ls_row-rating ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.0' ) + act = CONV decfloat34( ls_row-avg_pm ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '30.0' ) + act = CONV decfloat34( ls_row-deviation ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1060/seed/reading.tabl.asabap b/tasks_gen/train/G1060/seed/reading.tabl.asabap new file mode 100644 index 0000000..b1c91d7 --- /dev/null +++ b/tasks_gen/train/G1060/seed/reading.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Fine dust reading' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}reading { + key client : abap.clnt not null; + key station_id : abap.char(10) not null; + key reading_no : abap.int4 not null; + pm_value : abap.dec(5,1); +} diff --git a/tasks_gen/train/G1060/seed/station.tabl.asabap b/tasks_gen/train/G1060/seed/station.tabl.asabap new file mode 100644 index 0000000..df50d2c --- /dev/null +++ b/tasks_gen/train/G1060/seed/station.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Measuring station' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}station { + key client : abap.clnt not null; + key station_id : abap.char(10) not null; + station_name : abap.char(20); + limit_pm : abap.dec(5,1); +} diff --git a/tasks_gen/train/G1060/spec.md b/tasks_gen/train/G1060/spec.md new file mode 100644 index 0000000..7223bb9 --- /dev/null +++ b/tasks_gen/train/G1060/spec.md @@ -0,0 +1,44 @@ +# 1. Goal +Show the air quality of each measuring station. A dashboard and an OData service +will read the values, so the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}STATION (measuring station), package $TMP. Key: STATION_ID (CHAR 10). + Fields: STATION_NAME (CHAR 20), LIMIT_PM (DEC 5,1, the permitted fine dust + value of the station). +- Table {{P}}READING (single fine dust reading), package $TMP. Key: STATION_ID + (CHAR 10), READING_NO (INT4). Fields: PM_VALUE (DEC 5,1, the measured fine + dust value). + +# 4. Contract +- Create the CDS view entity {{P}}I_AIR_QUALITY in package $TMP. +- Elements, with these names: STATION_ID (key), STATION_NAME, READING_COUNT, + AVG_PM, MAX_PM, RATING, DEVIATION. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each station that has at least one reading. Do not show a + station without readings. +2. STATION_NAME is the name of the station. +3. READING_COUNT is the number of readings of the station. +4. AVG_PM is the average PM_VALUE of the readings of the station, with 1 decimal + place. +5. MAX_PM is the highest PM_VALUE of the station. +6. RATING is the class of the station: + - 'HIGH' if MAX_PM is greater than LIMIT_PM, + - 'MEDIUM' if MAX_PM is greater than 80 percent of LIMIT_PM, + - 'LOW' in all other cases. +7. DEVIATION is the distance between MAX_PM and LIMIT_PM, as a positive number. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. No ATC priority 1 or priority 2 findings. +- Out of scope: do not change {{P}}STATION and {{P}}READING. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1060/task.json b/tasks_gen/train/G1060/task.json new file mode 100644 index 0000000..e94630d --- /dev/null +++ b/tasks_gen/train/G1060/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1060", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}STATION", + "file": "seed/station.tabl.asabap", + "description": "Measuring station with its fine dust limit" + }, + { + "type": "TABL", + "name": "{{P}}READING", + "file": "seed/reading.tabl.asabap", + "description": "Single fine dust reading of a station" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_AIR_QUALITY", + "fields": [ + "STATION_ID", + "STATION_NAME", + "READING_COUNT", + "AVG_PM", + "MAX_PM", + "RATING", + "DEVIATION" + ] + } + ], + "out_of_scope": [ + "{{P}}STATION", + "{{P}}READING" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_AIR_QUALITY", + "file": "reference/i_air_quality.ddls.asddls", + "description": "Air quality per measuring station" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "T41 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1061/faulty/m0_watch_order_pool.clas.abap b/tasks_gen/train/G1061/faulty/m0_watch_order_pool.clas.abap new file mode 100644 index 0000000..0392702 --- /dev/null +++ b/tasks_gen/train/G1061/faulty/m0_watch_order_pool.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}watch_order_pool DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_watch_order. + + PRIVATE SECTION. + TYPES ty_orders TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_order + WITH UNIQUE KEY order_id. + TYPES ty_totals TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_customer_total + WITH UNIQUE KEY customer. + + METHODS unique_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + RETURNING VALUE(rt_orders) TYPE ty_orders. + METHODS add_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + CHANGING ct_orders TYPE ty_orders. +ENDCLASS. + + +CLASS {{p}}watch_order_pool IMPLEMENTATION. + METHOD {{p}}if_watch_order~merge_orders. + DATA(lt_merged) = unique_orders( it_intake ). + add_orders( EXPORTING it_orders = it_workshop + CHANGING ct_orders = lt_merged ). + rt_merged = lt_merged. + ENDMETHOD. + + METHOD {{p}}if_watch_order~open_orders. + DATA(lt_orders) = unique_orders( it_orders ). + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status <> {{p}}if_watch_order=>c_status-open. + APPEND ls_order TO rt_open. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_watch_order~customer_totals. + DATA(lt_orders) = unique_orders( it_orders ). + DATA lt_totals TYPE ty_totals. + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + READ TABLE lt_totals ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY customer = ls_order-customer. + IF sy-subrc = 0. + -total_min = -total_min + ls_order-duration_min. + ELSE. + INSERT VALUE {{p}}if_watch_order=>ty_customer_total( + customer = ls_order-customer + total_min = ls_order-duration_min ) + INTO TABLE lt_totals. + ENDIF. + ENDIF. + ENDLOOP. + rt_totals = lt_totals. + ENDMETHOD. + + METHOD {{p}}if_watch_order~find_order. + DATA(lt_orders) = unique_orders( it_orders ). + DATA ls_found TYPE {{p}}if_watch_order=>ty_order. + READ TABLE lt_orders INTO ls_found WITH TABLE KEY order_id = iv_order_id. + rs_order = ls_found. + ENDMETHOD. + + METHOD add_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF line_exists( ct_orders[ order_id = ls_order-order_id ] ). + CONTINUE. + ENDIF. + INSERT ls_order INTO TABLE ct_orders. + ENDLOOP. + ENDMETHOD. + + METHOD unique_orders. + DATA lt_orders TYPE ty_orders. + add_orders( EXPORTING it_orders = it_orders + CHANGING ct_orders = lt_orders ). + rt_orders = lt_orders. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/faulty/m1_watch_order_pool.clas.abap b/tasks_gen/train/G1061/faulty/m1_watch_order_pool.clas.abap new file mode 100644 index 0000000..57bb336 --- /dev/null +++ b/tasks_gen/train/G1061/faulty/m1_watch_order_pool.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}watch_order_pool DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_watch_order. + + PRIVATE SECTION. + TYPES ty_orders TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_order + WITH UNIQUE KEY order_id. + TYPES ty_totals TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_customer_total + WITH UNIQUE KEY customer. + + METHODS unique_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + RETURNING VALUE(rt_orders) TYPE ty_orders. + METHODS add_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + CHANGING ct_orders TYPE ty_orders. +ENDCLASS. + + +CLASS {{p}}watch_order_pool IMPLEMENTATION. + METHOD {{p}}if_watch_order~merge_orders. + DATA(lt_merged) = unique_orders( it_intake ). + add_orders( EXPORTING it_orders = it_workshop + CHANGING ct_orders = lt_merged ). + rt_merged = lt_merged. + ENDMETHOD. + + METHOD {{p}}if_watch_order~open_orders. + DATA(lt_orders) = unique_orders( it_orders ). + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + APPEND ls_order TO rt_open. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_watch_order~customer_totals. + DATA(lt_orders) = unique_orders( it_orders ). + DATA lt_totals TYPE ty_totals. + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status <> {{p}}if_watch_order=>c_status-open. + READ TABLE lt_totals ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY customer = ls_order-customer. + IF sy-subrc = 0. + -total_min = -total_min + ls_order-duration_min. + ELSE. + INSERT VALUE {{p}}if_watch_order=>ty_customer_total( + customer = ls_order-customer + total_min = ls_order-duration_min ) + INTO TABLE lt_totals. + ENDIF. + ENDIF. + ENDLOOP. + rt_totals = lt_totals. + ENDMETHOD. + + METHOD {{p}}if_watch_order~find_order. + DATA(lt_orders) = unique_orders( it_orders ). + DATA ls_found TYPE {{p}}if_watch_order=>ty_order. + READ TABLE lt_orders INTO ls_found WITH TABLE KEY order_id = iv_order_id. + rs_order = ls_found. + ENDMETHOD. + + METHOD add_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF line_exists( ct_orders[ order_id = ls_order-order_id ] ). + CONTINUE. + ENDIF. + INSERT ls_order INTO TABLE ct_orders. + ENDLOOP. + ENDMETHOD. + + METHOD unique_orders. + DATA lt_orders TYPE ty_orders. + add_orders( EXPORTING it_orders = it_orders + CHANGING ct_orders = lt_orders ). + rt_orders = lt_orders. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/faulty/m2_watch_order_pool.clas.abap b/tasks_gen/train/G1061/faulty/m2_watch_order_pool.clas.abap new file mode 100644 index 0000000..1cd2ded --- /dev/null +++ b/tasks_gen/train/G1061/faulty/m2_watch_order_pool.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}watch_order_pool DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_watch_order. + + PRIVATE SECTION. + TYPES ty_orders TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_order + WITH UNIQUE KEY order_id. + TYPES ty_totals TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_customer_total + WITH UNIQUE KEY customer. + + METHODS unique_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + RETURNING VALUE(rt_orders) TYPE ty_orders. + METHODS add_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + CHANGING ct_orders TYPE ty_orders. +ENDCLASS. + + +CLASS {{p}}watch_order_pool IMPLEMENTATION. + METHOD {{p}}if_watch_order~merge_orders. + DATA(lt_merged) = unique_orders( it_intake ). + add_orders( EXPORTING it_orders = it_workshop + CHANGING ct_orders = lt_merged ). + rt_merged = lt_merged. + ENDMETHOD. + + METHOD {{p}}if_watch_order~open_orders. + DATA(lt_orders) = unique_orders( it_orders ). + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + APPEND ls_order TO rt_open. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_watch_order~customer_totals. + DATA(lt_orders) = unique_orders( it_orders ). + DATA lt_totals TYPE ty_totals. + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + READ TABLE lt_totals ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY customer = ls_order-customer. + IF sy-subrc = 0. + -total_min = -total_min - ls_order-duration_min. + ELSE. + INSERT VALUE {{p}}if_watch_order=>ty_customer_total( + customer = ls_order-customer + total_min = ls_order-duration_min ) + INTO TABLE lt_totals. + ENDIF. + ENDIF. + ENDLOOP. + rt_totals = lt_totals. + ENDMETHOD. + + METHOD {{p}}if_watch_order~find_order. + DATA(lt_orders) = unique_orders( it_orders ). + DATA ls_found TYPE {{p}}if_watch_order=>ty_order. + READ TABLE lt_orders INTO ls_found WITH TABLE KEY order_id = iv_order_id. + rs_order = ls_found. + ENDMETHOD. + + METHOD add_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF line_exists( ct_orders[ order_id = ls_order-order_id ] ). + CONTINUE. + ENDIF. + INSERT ls_order INTO TABLE ct_orders. + ENDLOOP. + ENDMETHOD. + + METHOD unique_orders. + DATA lt_orders TYPE ty_orders. + add_orders( EXPORTING it_orders = it_orders + CHANGING ct_orders = lt_orders ). + rt_orders = lt_orders. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/generation.json b/tasks_gen/train/G1061/generation.json new file mode 100644 index 0000000..9bf5aac --- /dev/null +++ b/tasks_gen/train/G1061/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1061", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 88.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1061/hidden/watch_order_hidden.clas.abap b/tasks_gen/train/G1061/hidden/watch_order_hidden.clas.abap new file mode 100644 index 0000000..9808dfc --- /dev/null +++ b/tasks_gen/train/G1061/hidden/watch_order_hidden.clas.abap @@ -0,0 +1,142 @@ +CLASS {{p}}watch_order_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_watch_order. + METHODS setup. + METHODS merge_keeps_intake_first FOR TESTING. + METHODS merge_sorts_by_order_id FOR TESTING. + METHODS merge_removes_duplicates FOR TESTING. + METHODS merge_empty_input FOR TESTING. + METHODS open_orders_filters_status FOR TESTING. + METHODS open_orders_removes_dups FOR TESTING. + METHODS totals_sum_open_only FOR TESTING. + METHODS totals_sorted_by_customer FOR TESTING. + METHODS totals_ignore_duplicate FOR TESTING. + METHODS find_order_returns_first FOR TESTING. + METHODS find_order_not_found FOR TESTING. +ENDCLASS. + + +CLASS {{p}}watch_order_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}watch_order_pool( ). + ENDMETHOD. + + METHOD merge_keeps_intake_first. + DATA(lt_intake) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'A1' customer = 'MUELLER' duration_min = 30 status = 'O' ) + ( order_id = 'A2' customer = 'SCHMIDT' duration_min = 20 status = 'O' ) ). + DATA(lt_workshop) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'A2' customer = 'OTHER' duration_min = 99 status = 'C' ) + ( order_id = 'A3' customer = 'WEBER' duration_min = 10 status = 'O' ) ). + DATA(lt_merged) = mo_cut->merge_orders( it_intake = lt_intake + it_workshop = lt_workshop ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_merged ) ). + cl_abap_unit_assert=>assert_equals( exp = 'SCHMIDT' act = lt_merged[ 2 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_merged[ 2 ]-duration_min ). + ENDMETHOD. + + METHOD merge_sorts_by_order_id. + DATA(lt_intake) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'B3' customer = 'GAMMA' duration_min = 1 status = 'O' ) + ( order_id = 'B1' customer = 'ALPHA' duration_min = 2 status = 'O' ) ). + DATA(lt_workshop) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'B2' customer = 'BETA' duration_min = 3 status = 'O' ) ). + DATA(lt_merged) = mo_cut->merge_orders( it_intake = lt_intake + it_workshop = lt_workshop ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_merged[ 1 ]-order_id ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_merged[ 2 ]-order_id ). + cl_abap_unit_assert=>assert_equals( exp = 'B3' act = lt_merged[ 3 ]-order_id ). + ENDMETHOD. + + METHOD merge_removes_duplicates. + DATA(lt_intake) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'C1' customer = 'FIRST' duration_min = 5 status = 'O' ) + ( order_id = 'C1' customer = 'SECOND' duration_min = 7 status = 'O' ) ). + DATA(lt_workshop) = VALUE {{p}}if_watch_order=>tt_orders( ). + DATA(lt_merged) = mo_cut->merge_orders( it_intake = lt_intake + it_workshop = lt_workshop ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_merged ) ). + cl_abap_unit_assert=>assert_equals( exp = 'FIRST' act = lt_merged[ 1 ]-customer ). + ENDMETHOD. + + METHOD merge_empty_input. + DATA(lt_merged) = mo_cut->merge_orders( + it_intake = VALUE {{p}}if_watch_order=>tt_orders( ) + it_workshop = VALUE {{p}}if_watch_order=>tt_orders( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_merged ) ). + ENDMETHOD. + + METHOD open_orders_filters_status. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'D1' customer = 'ALPHA' duration_min = 1 status = 'C' ) + ( order_id = 'D2' customer = 'BETA' duration_min = 2 status = 'O' ) + ( order_id = 'D3' customer = 'GAMMA' duration_min = 3 status = 'O' ) ). + DATA(lt_open) = mo_cut->open_orders( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_open ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D2' act = lt_open[ 1 ]-order_id ). + cl_abap_unit_assert=>assert_equals( exp = 'D3' act = lt_open[ 2 ]-order_id ). + ENDMETHOD. + + METHOD open_orders_removes_dups. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'E1' customer = 'FIRST' duration_min = 1 status = 'O' ) + ( order_id = 'E1' customer = 'SECOND' duration_min = 2 status = 'O' ) ). + DATA(lt_open) = mo_cut->open_orders( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_open ) ). + cl_abap_unit_assert=>assert_equals( exp = 'FIRST' act = lt_open[ 1 ]-customer ). + ENDMETHOD. + + METHOD totals_sum_open_only. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'F1' customer = 'ALPHA' duration_min = 10 status = 'O' ) + ( order_id = 'F2' customer = 'ALPHA' duration_min = 5 status = 'O' ) + ( order_id = 'F3' customer = 'ALPHA' duration_min = 100 status = 'C' ) + ( order_id = 'F4' customer = 'BETA' duration_min = 7 status = 'O' ) ). + DATA(lt_totals) = mo_cut->customer_totals( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_totals[ 1 ]-total_min ). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lt_totals[ 2 ]-total_min ). + ENDMETHOD. + + METHOD totals_sorted_by_customer. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'G1' customer = 'ZETA' duration_min = 1 status = 'O' ) + ( order_id = 'G2' customer = 'ALPHA' duration_min = 2 status = 'O' ) + ( order_id = 'G3' customer = 'MIKE' duration_min = 3 status = 'O' ) ). + DATA(lt_totals) = mo_cut->customer_totals( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_totals[ 1 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 'MIKE' act = lt_totals[ 2 ]-customer ). + cl_abap_unit_assert=>assert_equals( exp = 'ZETA' act = lt_totals[ 3 ]-customer ). + ENDMETHOD. + + METHOD totals_ignore_duplicate. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'H1' customer = 'ALPHA' duration_min = 10 status = 'O' ) + ( order_id = 'H1' customer = 'ALPHA' duration_min = 10 status = 'O' ) ). + DATA(lt_totals) = mo_cut->customer_totals( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_totals[ 1 ]-total_min ). + ENDMETHOD. + + METHOD find_order_returns_first. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'I1' customer = 'FIRST' duration_min = 4 status = 'O' ) + ( order_id = 'I1' customer = 'SECOND' duration_min = 9 status = 'O' ) + ( order_id = 'I2' customer = 'OTHER' duration_min = 1 status = 'O' ) ). + DATA(ls_order) = mo_cut->find_order( it_orders = lt_orders + iv_order_id = 'I1' ). + cl_abap_unit_assert=>assert_equals( exp = 'FIRST' act = ls_order-customer ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_order-duration_min ). + ENDMETHOD. + + METHOD find_order_not_found. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'J1' customer = 'ALPHA' duration_min = 1 status = 'O' ) ). + DATA(ls_order) = mo_cut->find_order( it_orders = lt_orders + iv_order_id = 'ZZ' ). + cl_abap_unit_assert=>assert_initial( ls_order-order_id ). + cl_abap_unit_assert=>assert_initial( ls_order-customer ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_order-duration_min ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/mutation.json b/tasks_gen/train/G1061/mutation.json new file mode 100644 index 0000000..634246b --- /dev/null +++ b/tasks_gen/train/G1061/mutation.json @@ -0,0 +1,46 @@ +{ + "task": "G1061", + "mutants": [ + { + "object": "{{P}}WATCH_ORDER_POOL", + "mutant": "line 31: = -> <> (eq)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "OPEN_ORDERS_FILTERS_STATUS", + "OPEN_ORDERS_REMOVES_DUPS" + ] + }, + { + "object": "{{P}}WATCH_ORDER_POOL", + "mutant": "line 41: = -> <> (eq)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "TOTALS_IGNORE_DUPLICATE", + "TOTALS_SORTED_BY_CUSTOMER", + "TOTALS_SUM_OPEN_ONLY" + ] + }, + { + "object": "{{P}}WATCH_ORDER_POOL", + "mutant": "line 45: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "TOTALS_SUM_OPEN_ONLY" + ] + }, + { + "object": "{{P}}WATCH_ORDER_POOL", + "mutant": "line 66: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1061/reference/watch_order_pool.clas.abap b/tasks_gen/train/G1061/reference/watch_order_pool.clas.abap new file mode 100644 index 0000000..539414f --- /dev/null +++ b/tasks_gen/train/G1061/reference/watch_order_pool.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}watch_order_pool DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_watch_order. + + PRIVATE SECTION. + TYPES ty_orders TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_order + WITH UNIQUE KEY order_id. + TYPES ty_totals TYPE SORTED TABLE OF {{p}}if_watch_order=>ty_customer_total + WITH UNIQUE KEY customer. + + METHODS unique_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + RETURNING VALUE(rt_orders) TYPE ty_orders. + METHODS add_orders + IMPORTING it_orders TYPE {{p}}if_watch_order=>tt_orders + CHANGING ct_orders TYPE ty_orders. +ENDCLASS. + + +CLASS {{p}}watch_order_pool IMPLEMENTATION. + METHOD {{p}}if_watch_order~merge_orders. + DATA(lt_merged) = unique_orders( it_intake ). + add_orders( EXPORTING it_orders = it_workshop + CHANGING ct_orders = lt_merged ). + rt_merged = lt_merged. + ENDMETHOD. + + METHOD {{p}}if_watch_order~open_orders. + DATA(lt_orders) = unique_orders( it_orders ). + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + APPEND ls_order TO rt_open. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_watch_order~customer_totals. + DATA(lt_orders) = unique_orders( it_orders ). + DATA lt_totals TYPE ty_totals. + LOOP AT lt_orders INTO DATA(ls_order). + IF ls_order-status = {{p}}if_watch_order=>c_status-open. + READ TABLE lt_totals ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY customer = ls_order-customer. + IF sy-subrc = 0. + -total_min = -total_min + ls_order-duration_min. + ELSE. + INSERT VALUE {{p}}if_watch_order=>ty_customer_total( + customer = ls_order-customer + total_min = ls_order-duration_min ) + INTO TABLE lt_totals. + ENDIF. + ENDIF. + ENDLOOP. + rt_totals = lt_totals. + ENDMETHOD. + + METHOD {{p}}if_watch_order~find_order. + DATA(lt_orders) = unique_orders( it_orders ). + DATA ls_found TYPE {{p}}if_watch_order=>ty_order. + READ TABLE lt_orders INTO ls_found WITH TABLE KEY order_id = iv_order_id. + rs_order = ls_found. + ENDMETHOD. + + METHOD add_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF line_exists( ct_orders[ order_id = ls_order-order_id ] ). + CONTINUE. + ENDIF. + INSERT ls_order INTO TABLE ct_orders. + ENDLOOP. + ENDMETHOD. + + METHOD unique_orders. + DATA lt_orders TYPE ty_orders. + add_orders( EXPORTING it_orders = it_orders + CHANGING ct_orders = lt_orders ). + rt_orders = lt_orders. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/reference/watch_order_pool.testclasses.abap b/tasks_gen/train/G1061/reference/watch_order_pool.testclasses.abap new file mode 100644 index 0000000..07c7e60 --- /dev/null +++ b/tasks_gen/train/G1061/reference/watch_order_pool.testclasses.abap @@ -0,0 +1,63 @@ +CLASS ltc_watch_order_pool DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_watch_order. + METHODS setup. + METHODS merge_keeps_first_entry FOR TESTING. + METHODS open_orders_are_sorted FOR TESTING. + METHODS totals_only_open_orders FOR TESTING. + METHODS find_order_returns_entry FOR TESTING. + METHODS find_order_unknown_is_empty FOR TESTING. +ENDCLASS. + + +CLASS ltc_watch_order_pool IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}watch_order_pool( ). + ENDMETHOD. + + METHOD merge_keeps_first_entry. + DATA(lt_intake) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'A1' customer = 'MUELLER' duration_min = 30 status = 'O' ) ). + DATA(lt_workshop) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'A1' customer = 'OTHER' duration_min = 99 status = 'O' ) ). + DATA(lt_merged) = mo_cut->merge_orders( it_intake = lt_intake + it_workshop = lt_workshop ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_merged ) ). + cl_abap_unit_assert=>assert_equals( exp = 'MUELLER' act = lt_merged[ 1 ]-customer ). + ENDMETHOD. + + METHOD open_orders_are_sorted. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'B2' customer = 'BETA' duration_min = 2 status = 'O' ) + ( order_id = 'B1' customer = 'ALPHA' duration_min = 1 status = 'O' ) + ( order_id = 'B3' customer = 'GAMMA' duration_min = 3 status = 'C' ) ). + DATA(lt_open) = mo_cut->open_orders( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_open ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_open[ 1 ]-order_id ). + ENDMETHOD. + + METHOD totals_only_open_orders. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'C1' customer = 'ALPHA' duration_min = 10 status = 'O' ) + ( order_id = 'C2' customer = 'ALPHA' duration_min = 90 status = 'C' ) ). + DATA(lt_totals) = mo_cut->customer_totals( lt_orders ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_totals[ 1 ]-total_min ). + ENDMETHOD. + + METHOD find_order_returns_entry. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'D1' customer = 'ALPHA' duration_min = 5 status = 'O' ) ). + DATA(ls_order) = mo_cut->find_order( it_orders = lt_orders + iv_order_id = 'D1' ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_order-customer ). + ENDMETHOD. + + METHOD find_order_unknown_is_empty. + DATA(lt_orders) = VALUE {{p}}if_watch_order=>tt_orders( + ( order_id = 'E1' customer = 'ALPHA' duration_min = 5 status = 'O' ) ). + DATA(ls_order) = mo_cut->find_order( it_orders = lt_orders + iv_order_id = 'E9' ). + cl_abap_unit_assert=>assert_initial( ls_order-order_id ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1061/seed/if_watch_order.intf.abap b/tasks_gen/train/G1061/seed/if_watch_order.intf.abap new file mode 100644 index 0000000..e716af0 --- /dev/null +++ b/tasks_gen/train/G1061/seed/if_watch_order.intf.abap @@ -0,0 +1,38 @@ +INTERFACE {{p}}if_watch_order PUBLIC. + TYPES ty_order_id TYPE c LENGTH 10. + TYPES ty_customer TYPE c LENGTH 20. + TYPES ty_status TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_order, + order_id TYPE ty_order_id, + customer TYPE ty_customer, + duration_min TYPE i, + status TYPE ty_status, + END OF ty_order. + TYPES tt_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY. + TYPES: + BEGIN OF ty_customer_total, + customer TYPE ty_customer, + total_min TYPE i, + END OF ty_customer_total. + TYPES tt_customer_totals TYPE STANDARD TABLE OF ty_customer_total WITH EMPTY KEY. + CONSTANTS: + BEGIN OF c_status, + open TYPE ty_status VALUE 'O', + closed TYPE ty_status VALUE 'C', + END OF c_status. + METHODS merge_orders + IMPORTING it_intake TYPE tt_orders + it_workshop TYPE tt_orders + RETURNING VALUE(rt_merged) TYPE tt_orders. + METHODS open_orders + IMPORTING it_orders TYPE tt_orders + RETURNING VALUE(rt_open) TYPE tt_orders. + METHODS customer_totals + IMPORTING it_orders TYPE tt_orders + RETURNING VALUE(rt_totals) TYPE tt_customer_totals. + METHODS find_order + IMPORTING it_orders TYPE tt_orders + iv_order_id TYPE ty_order_id + RETURNING VALUE(rs_order) TYPE ty_order. +ENDINTERFACE. diff --git a/tasks_gen/train/G1061/spec.md b/tasks_gen/train/G1061/spec.md new file mode 100644 index 0000000..fab9877 --- /dev/null +++ b/tasks_gen/train/G1061/spec.md @@ -0,0 +1,77 @@ +# 1. Goal +A watch and clock workshop repairs customer orders. Two intake points send +their order lists: the front desk and a partner shop. The workshop program +needs one merged list of all orders and the total repair time of each +customer. The class {{P}}WATCH_ORDER_POOL does this work. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_WATCH_ORDER exists in package $TMP. It is active. +- The interface contains the types TY_ORDER_ID (CHAR 10), TY_CUSTOMER + (CHAR 20), TY_STATUS (CHAR 1), TY_ORDER, TT_ORDERS, TY_CUSTOMER_TOTAL and + TT_CUSTOMER_TOTALS. Read the interface for the exact definition. +- TY_ORDER has the components ORDER_ID, CUSTOMER, DURATION_MIN (integer) and + STATUS. +- TY_CUSTOMER_TOTAL has the components CUSTOMER and TOTAL_MIN (integer). +- The interface contains the constant structure C_STATUS with the components + OPEN (value 'O') and CLOSED (value 'C'). +- All objects are in package $TMP. + +# 4. Contract +- Create the class {{P}}WATCH_ORDER_POOL in package $TMP. +- The class is public and final. It has a public constructor without + parameters. +- The class implements the interface {{P}}IF_WATCH_ORDER. +- Do not add other public methods. + +The interface methods do these tasks: +- merge_orders: merge the two intake lists into one order list. +- open_orders: select the open orders of one order list. +- customer_totals: calculate the total repair time of each customer. +- find_order: find one order in a list by its order ID. + +# 5. Business rules +General: +- An order ID is never empty. A customer name is never empty. +- DURATION_MIN is never negative. + +merge_orders: +1. Read the orders of IT_INTAKE first. Then read the orders of IT_WORKSHOP. +2. If one order ID occurs more than once, keep the first occurrence and + ignore all later occurrences. This rule holds inside one list and across + the two lists. +3. The result contains all orders. The result is sorted in ascending order of + ORDER_ID. + +open_orders: +1. Use the orders of IT_ORDERS. +2. If one order ID occurs more than once, keep the first occurrence. +3. Keep only the orders with STATUS = C_STATUS-OPEN. +4. The result is sorted in ascending order of ORDER_ID. + +customer_totals: +1. Use only the orders of IT_ORDERS with STATUS = C_STATUS-OPEN. +2. If one order ID occurs more than once, count it one time only (the first + occurrence). +3. Sum DURATION_MIN for each customer. +4. The result has one row for each customer with at least one open order. +5. The result is sorted in ascending order of CUSTOMER. + +find_order: +1. Search IT_ORDERS for the order with ORDER_ID = IV_ORDER_ID. +2. If one order ID occurs more than once, return the first occurrence. +3. If no order has this order ID, return a structure with initial components: + ORDER_ID initial, CUSTOMER initial, DURATION_MIN = 0, STATUS initial. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- 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_WATCH_ORDER. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1061/task.json b/tasks_gen/train/G1061/task.json new file mode 100644 index 0000000..895e5e5 --- /dev/null +++ b/tasks_gen/train/G1061/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1061", + "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_WATCH_ORDER", + "file": "seed/if_watch_order.intf.abap", + "description": "Order types, status constants and the method contract of the watch workshop" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}WATCH_ORDER_POOL", + "implements": "{{P}}IF_WATCH_ORDER" + } + ], + "out_of_scope": [ + "{{P}}IF_WATCH_ORDER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}WATCH_ORDER_HIDDEN", + "file": "hidden/watch_order_hidden.clas.abap", + "description": "Hidden tests for the merged order list, the open orders, the customer totals and the order lookup" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}WATCH_ORDER_POOL", + "file": "reference/watch_order_pool.clas.abap", + "description": "Reference solution that works with sorted and keyed internal tables", + "testclasses_file": "reference/watch_order_pool.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "internal_table_kind" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1062/faulty/m0_parking_pricer.clas.abap b/tasks_gen/train/G1062/faulty/m0_parking_pricer.clas.abap new file mode 100644 index 0000000..97ac733 --- /dev/null +++ b/tasks_gen/train/G1062/faulty/m0_parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket <> abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes <= c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) * ls_tariff-rate. + IF rv_fee > ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/faulty/m1_parking_pricer.clas.abap b/tasks_gen/train/G1062/faulty/m1_parking_pricer.clas.abap new file mode 100644 index 0000000..3418ccd --- /dev/null +++ b/tasks_gen/train/G1062/faulty/m1_parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket = abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes > c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) * ls_tariff-rate. + IF rv_fee > ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/faulty/m2_parking_pricer.clas.abap b/tasks_gen/train/G1062/faulty/m2_parking_pricer.clas.abap new file mode 100644 index 0000000..fba5995 --- /dev/null +++ b/tasks_gen/train/G1062/faulty/m2_parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket = abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes <= c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) / ls_tariff-rate. + IF rv_fee > ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/faulty/m3_parking_pricer.clas.abap b/tasks_gen/train/G1062/faulty/m3_parking_pricer.clas.abap new file mode 100644 index 0000000..ccb6536 --- /dev/null +++ b/tasks_gen/train/G1062/faulty/m3_parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket = abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes <= c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) * ls_tariff-rate. + IF rv_fee <= ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/faulty/m4_parking_pricer.clas.abap b/tasks_gen/train/G1062/faulty/m4_parking_pricer.clas.abap new file mode 100644 index 0000000..1bc2ffc --- /dev/null +++ b/tasks_gen/train/G1062/faulty/m4_parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket = abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes <= c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) * ls_tariff-rate. + IF rv_fee > ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour = 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/generation.json b/tasks_gen/train/G1062/generation.json new file mode 100644 index 0000000..42b1c2e --- /dev/null +++ b/tasks_gen/train/G1062/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1062", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1062/hidden/parking_hidden.clas.abap b/tasks_gen/train/G1062/hidden/parking_hidden.clas.abap new file mode 100644 index 0000000..e76d4fa --- /dev/null +++ b/tasks_gen/train/G1062/hidden/parking_hidden.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}parking_hidden DEFINITION + PUBLIC + FINAL + CREATE PUBLIC + FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}parking_pricer. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_expected TYPE {{p}}parking_pricer=>ty_fee + iv_minutes TYPE i + iv_vehicle TYPE {{p}}parking_pricer=>ty_vehicle + iv_lost TYPE abap_bool DEFAULT abap_false. + + METHODS car_short_stay FOR TESTING. + METHODS grace_period_free FOR TESTING. + METHODS just_after_grace FOR TESTING. + METHODS negative_minutes_free FOR TESTING. + METHODS started_hour_rounds_up FOR TESTING. + METHODS motorcycle_and_van_rates FOR TESTING. + METHODS daily_caps FOR TESTING. + METHODS lost_ticket_fees FOR TESTING. + METHODS unknown_vehicle_free FOR TESTING. + METHODS unknown_vehicle_lost FOR TESTING. +ENDCLASS. + + +CLASS {{p}}parking_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}parking_pricer( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_fee( iv_minutes = iv_minutes + iv_vehicle = iv_vehicle + iv_lost_ticket = iv_lost ) ). + ENDMETHOD. + + METHOD car_short_stay. + assert_fee( iv_expected = '4.00' + iv_minutes = 90 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD grace_period_free. + assert_fee( iv_expected = '0.00' + iv_minutes = 15 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD just_after_grace. + assert_fee( iv_expected = '2.00' + iv_minutes = 16 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD negative_minutes_free. + assert_fee( iv_expected = '0.00' + iv_minutes = -30 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD started_hour_rounds_up. + assert_fee( iv_expected = '4.00' + iv_minutes = 61 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD motorcycle_and_van_rates. + assert_fee( iv_expected = '2.00' + iv_minutes = 120 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_motorcycle ). + assert_fee( iv_expected = '6.00' + iv_minutes = 120 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_van ). + ENDMETHOD. + + METHOD daily_caps. + assert_fee( iv_expected = '20.00' + iv_minutes = 900 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + assert_fee( iv_expected = '24.00' + iv_minutes = 600 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_van ). + assert_fee( iv_expected = '10.00' + iv_minutes = 1200 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_motorcycle ). + ENDMETHOD. + + METHOD lost_ticket_fees. + assert_fee( iv_expected = '25.00' + iv_minutes = 5 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car + iv_lost = abap_true ). + assert_fee( iv_expected = '15.00' + iv_minutes = 120 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_motorcycle + iv_lost = abap_true ). + assert_fee( iv_expected = '35.00' + iv_minutes = 120 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_van + iv_lost = abap_true ). + ENDMETHOD. + + METHOD unknown_vehicle_free. + assert_fee( iv_expected = '0.00' + iv_minutes = 120 + iv_vehicle = 'X' ). + ENDMETHOD. + + METHOD unknown_vehicle_lost. + assert_fee( iv_expected = '25.00' + iv_minutes = 120 + iv_vehicle = 'X' + iv_lost = abap_true ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/mutation.json b/tasks_gen/train/G1062/mutation.json new file mode 100644 index 0000000..82f1217 --- /dev/null +++ b/tasks_gen/train/G1062/mutation.json @@ -0,0 +1,80 @@ +{ + "task": "G1062", + "mutants": [ + { + "object": "{{P}}PARKING_PRICER", + "mutant": "line 61: = -> <> (eq)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "CAR_SHORT_STAY", + "DAILY_CAPS", + "GRACE_PERIOD_FREE", + "JUST_AFTER_GRACE", + "LOST_TICKET_FEES", + "MOTORCYCLE_AND_VAN_RATES", + "NEGATIVE_MINUTES_FREE", + "STARTED_HOUR_ROUNDS_UP", + "UNKNOWN_VEHICLE_FREE", + "UNKNOWN_VEHICLE_LOST" + ] + }, + { + "object": "{{P}}PARKING_PRICER", + "mutant": "line 66: <= -> > (rel)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "CAR_SHORT_STAY", + "DAILY_CAPS", + "GRACE_PERIOD_FREE", + "JUST_AFTER_GRACE", + "MOTORCYCLE_AND_VAN_RATES", + "STARTED_HOUR_ROUNDS_UP" + ] + }, + { + "object": "{{P}}PARKING_PRICER", + "mutant": "line 71: * -> / (arith)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "CAR_SHORT_STAY", + "DAILY_CAPS", + "JUST_AFTER_GRACE", + "MOTORCYCLE_AND_VAN_RATES", + "STARTED_HOUR_ROUNDS_UP", + "UNKNOWN_VEHICLE_FREE" + ] + }, + { + "object": "{{P}}PARKING_PRICER", + "mutant": "line 72: > -> <= (rel)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "CAR_SHORT_STAY", + "DAILY_CAPS", + "JUST_AFTER_GRACE", + "MOTORCYCLE_AND_VAN_RATES", + "STARTED_HOUR_ROUNDS_UP" + ] + }, + { + "object": "{{P}}PARKING_PRICER", + "mutant": "line 108: <> -> = (rel)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "CAR_SHORT_STAY", + "JUST_AFTER_GRACE", + "MOTORCYCLE_AND_VAN_RATES", + "STARTED_HOUR_ROUNDS_UP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1062/reference/parking_pricer.clas.abap b/tasks_gen/train/G1062/reference/parking_pricer.clas.abap new file mode 100644 index 0000000..7b47075 --- /dev/null +++ b/tasks_gen/train/G1062/reference/parking_pricer.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + c_grace_minutes TYPE i VALUE 15, + c_minutes_per_hour TYPE i VALUE 60, + c_no_fee TYPE ty_fee VALUE '0.00', + c_car_rate TYPE ty_fee VALUE '2.00', + c_car_cap TYPE ty_fee VALUE '20.00', + c_car_lost_fee TYPE ty_fee VALUE '25.00', + c_moto_rate TYPE ty_fee VALUE '1.00', + c_moto_cap TYPE ty_fee VALUE '10.00', + c_moto_lost_fee TYPE ty_fee VALUE '15.00', + c_van_rate TYPE ty_fee VALUE '3.00', + c_van_cap TYPE ty_fee VALUE '24.00', + c_van_lost_fee TYPE ty_fee VALUE '35.00', + c_default_lost_fee TYPE ty_fee VALUE '25.00'. + + TYPES: + BEGIN OF ty_tariff, + vehicle TYPE ty_vehicle, + rate TYPE ty_fee, + cap TYPE ty_fee, + lost_fee TYPE ty_fee, + END OF ty_tariff, + tt_tariff TYPE HASHED TABLE OF ty_tariff WITH UNIQUE KEY vehicle. + + METHODS get_tariff + IMPORTING iv_vehicle TYPE ty_vehicle + RETURNING VALUE(rs_tariff) TYPE ty_tariff. + METHODS all_tariffs + RETURNING VALUE(rt_tariff) TYPE tt_tariff. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA(ls_tariff) = get_tariff( iv_vehicle ). + + IF iv_lost_ticket = abap_true. + rv_fee = ls_tariff-lost_fee. + RETURN. + ENDIF. + + IF iv_minutes <= c_grace_minutes. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + rv_fee = started_hours( iv_minutes ) * ls_tariff-rate. + IF rv_fee > ls_tariff-cap. + rv_fee = ls_tariff-cap. + ENDIF. + ENDMETHOD. + + METHOD get_tariff. + DATA(lt_tariff) = all_tariffs( ). + ASSIGN lt_tariff[ vehicle = iv_vehicle ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rs_tariff = . + ELSE. + rs_tariff = VALUE #( vehicle = iv_vehicle + rate = c_no_fee + cap = c_no_fee + lost_fee = c_default_lost_fee ). + ENDIF. + ENDMETHOD. + + METHOD all_tariffs. + rt_tariff = VALUE #( + ( vehicle = c_vehicle_car + rate = c_car_rate + cap = c_car_cap + lost_fee = c_car_lost_fee ) + ( vehicle = c_vehicle_motorcycle + rate = c_moto_rate + cap = c_moto_cap + lost_fee = c_moto_lost_fee ) + ( vehicle = c_vehicle_van + rate = c_van_rate + cap = c_van_cap + lost_fee = c_van_lost_fee ) ). + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV c_minutes_per_hour. + IF iv_minutes MOD c_minutes_per_hour <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/reference/parking_pricer.testclasses.abap b/tasks_gen/train/G1062/reference/parking_pricer.testclasses.abap new file mode 100644 index 0000000..6c00211 --- /dev/null +++ b/tasks_gen/train/G1062/reference/parking_pricer.testclasses.abap @@ -0,0 +1,72 @@ +CLASS ltc_parking_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}parking_pricer. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_expected TYPE {{p}}parking_pricer=>ty_fee + iv_minutes TYPE i + iv_vehicle TYPE {{p}}parking_pricer=>ty_vehicle + iv_lost TYPE abap_bool DEFAULT abap_false. + + METHODS car_short_stay FOR TESTING. + METHODS grace_period_free FOR TESTING. + METHODS started_hour_rounds_up FOR TESTING. + METHODS daily_cap_applies FOR TESTING. + METHODS lost_ticket_is_flat FOR TESTING. + METHODS unknown_vehicle_is_free FOR TESTING. +ENDCLASS. + + +CLASS ltc_parking_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}parking_pricer( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_fee( iv_minutes = iv_minutes + iv_vehicle = iv_vehicle + iv_lost_ticket = iv_lost ) ). + ENDMETHOD. + + METHOD car_short_stay. + assert_fee( iv_expected = '4.00' + iv_minutes = 90 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD grace_period_free. + assert_fee( iv_expected = '0.00' + iv_minutes = 10 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD started_hour_rounds_up. + assert_fee( iv_expected = '4.00' + iv_minutes = 61 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD daily_cap_applies. + assert_fee( iv_expected = '20.00' + iv_minutes = 900 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_car ). + ENDMETHOD. + + METHOD lost_ticket_is_flat. + assert_fee( iv_expected = '15.00' + iv_minutes = 10 + iv_vehicle = {{p}}parking_pricer=>c_vehicle_motorcycle + iv_lost = abap_true ). + ENDMETHOD. + + METHOD unknown_vehicle_is_free. + assert_fee( iv_expected = '0.00' + iv_minutes = 120 + iv_vehicle = 'X' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/seed/parking_pricer.clas.abap b/tasks_gen/train/G1062/seed/parking_pricer.clas.abap new file mode 100644 index 0000000..e2d153b --- /dev/null +++ b/tasks_gen/train/G1062/seed/parking_pricer.clas.abap @@ -0,0 +1,91 @@ +CLASS {{p}}parking_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_vehicle TYPE c LENGTH 1. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_vehicle_car TYPE ty_vehicle VALUE 'C', + c_vehicle_motorcycle TYPE ty_vehicle VALUE 'M', + c_vehicle_van TYPE ty_vehicle VALUE 'V'. + + METHODS calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + METHODS lost_ticket_fee + IMPORTING iv_vehicle TYPE ty_vehicle + EXPORTING ev_fee TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}parking_pricer IMPLEMENTATION. + METHOD calculate_fee. + DATA lv_minutes TYPE i. + DATA lv_hours TYPE i. + DATA lv_rate TYPE ty_fee. + DATA lv_cap TYPE ty_fee. + + IF iv_lost_ticket = abap_true. + " a lost ticket is charged with a flat fee + lost_ticket_fee( EXPORTING iv_vehicle = iv_vehicle + IMPORTING ev_fee = rv_fee ). + ELSE. + IF iv_minutes < 0. + lv_minutes = 0. + ELSE. + lv_minutes = iv_minutes. + ENDIF. + + IF lv_minutes <= 15. + " the first 15 minutes are free + rv_fee = 0. + ELSE. + lv_hours = lv_minutes DIV 60. + IF lv_minutes MOD 60 <> 0. + lv_hours = lv_hours + 1. + ENDIF. + IF lv_hours < 1. + lv_hours = 1. + ENDIF. + + IF iv_vehicle = 'C'. + lv_rate = '2.00'. + lv_cap = '20.00'. + ELSEIF iv_vehicle = 'M'. + lv_rate = '1.00'. + lv_cap = '10.00'. + ELSEIF iv_vehicle = 'V'. + lv_rate = '3.00'. + lv_cap = '24.00'. + ELSE. + lv_rate = '0.00'. + lv_cap = '0.00'. + ENDIF. + + rv_fee = lv_hours * lv_rate. + IF rv_fee > lv_cap. + rv_fee = lv_cap. + ENDIF. + ENDIF. + ENDIF. + ENDMETHOD. + + METHOD lost_ticket_fee. + IF iv_vehicle = 'C'. + ev_fee = '25.00'. + ELSEIF iv_vehicle = 'M'. + ev_fee = '15.00'. + ELSEIF iv_vehicle = 'V'. + ev_fee = '35.00'. + ELSE. + ev_fee = '25.00'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1062/spec.md b/tasks_gen/train/G1062/spec.md new file mode 100644 index 0000000..fc1eccc --- /dev/null +++ b/tasks_gen/train/G1062/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +Refactor the legacy class {{P}}PARKING_PRICER in package $TMP. A car park +operator uses the class to price a parking session. The class must keep its +public interface and must return the same fee as before for every input. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}PARKING_PRICER exists in package $TMP. It is active. +- The class is the contract object of this task. Refactor it in place. Keep the + class name, the package and the public interface. +- A checkout terminal calls the method calculate_fee for each parking session + and prints the returned fee. +- The legacy implementation contains the business rules. Read the legacy code + before you change it. + +# 4. Contract +- Class {{P}}PARKING_PRICER in package $TMP. Public, final, create public. +- Public type ty_vehicle (c length 1). +- Public type ty_fee (p length 8, 2 decimals). +- Public constants: + c_vehicle_car = 'C', c_vehicle_motorcycle = 'M', c_vehicle_van = 'V'. +- Public method: + calculate_fee + IMPORTING iv_minutes TYPE i + iv_vehicle TYPE ty_vehicle + iv_lost_ticket TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +- Do not add other public methods. Do not change the signature. + +# 5. Business rules +The behavior stays the same. The legacy implementation in package $TMP is the +only source of the rules. For every combination of iv_minutes, iv_vehicle and +iv_lost_ticket the refactored class must return the same fee as the legacy +class. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Methods below 40 statements. No magic numbers. + No deep nesting. No duplicated code. No comment that restates the code. No + global variables. +- Out of scope: the public interface of {{P}}PARKING_PRICER. Do not rename, + remove or add public types, constants or methods. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. +- No ATC priority 1 or priority 2 finding. diff --git a/tasks_gen/train/G1062/task.json b/tasks_gen/train/G1062/task.json new file mode 100644 index 0000000..a8cda8f --- /dev/null +++ b/tasks_gen/train/G1062/task.json @@ -0,0 +1,52 @@ +{ + "id": "G1062", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_PRICER", + "file": "seed/parking_pricer.clas.abap", + "description": "Legacy parking fee pricer with magic numbers, deep nesting and duplicated vehicle branches" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_PRICER" + } + ], + "out_of_scope": [ + "Public interface of {{P}}PARKING_PRICER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_HIDDEN", + "file": "hidden/parking_hidden.clas.abap", + "description": "Behavior tests for the parking fee" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_PRICER", + "file": "reference/parking_pricer.clas.abap", + "description": "Refactored parking fee pricer", + "testclasses_file": "reference/parking_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "magic_number", + "nesting_depth", + "duplicate_code" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1063/faulty/m2_toll_pricer.clas.abap b/tasks_gen/train/G1063/faulty/m2_toll_pricer.clas.abap new file mode 100644 index 0000000..bf540f1 --- /dev/null +++ b/tasks_gen/train/G1063/faulty/m2_toll_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}toll_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_toll_pricer. + METHODS constructor. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + vehicle_class TYPE {{p}}if_toll_pricer=>ty_class, + cents_per_km TYPE {{p}}if_toll_pricer=>ty_cents, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY vehicle_class. + + CONSTANTS: + c_min_toll TYPE {{p}}if_toll_pricer=>ty_cents VALUE 100, + c_max_km TYPE i VALUE 130, + c_discount TYPE i VALUE 10, + c_night_div TYPE i VALUE 4. + + DATA mt_rate TYPE tt_rate. + + METHODS fill_rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS validate + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_reason) TYPE {{p}}if_toll_pricer=>ty_reason. + METHODS base_toll + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_discount + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + iv_has_transponder TYPE abap_bool + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_night_surcharge + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. +ENDCLASS. + + +CLASS {{p}}toll_pricer IMPLEMENTATION. + METHOD constructor. + mt_rate = fill_rates( ). + ENDMETHOD. + + METHOD {{p}}if_toll_pricer~price_journey. + rs_result-reason = validate( is_journey ). + IF rs_result-reason <> {{p}}if_toll_pricer=>c_reason-ok. + RETURN. + ENDIF. + DATA(lv_amount) = base_toll( is_journey ). + lv_amount = apply_discount( iv_amount = lv_amount + iv_has_transponder = is_journey-has_transponder ). + lv_amount = apply_night_surcharge( iv_amount = lv_amount + is_journey = is_journey ). + rs_result-amount_cents = lv_amount. + ENDMETHOD. + + METHOD fill_rates. + rt_rate = VALUE #( ( vehicle_class = {{p}}if_toll_pricer=>c_class-motorcycle cents_per_km = 5 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-car cents_per_km = 10 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-van cents_per_km = 18 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-lorry cents_per_km = 30 ) ). + ENDMETHOD. + + METHOD validate. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + IF NOT line_exists( mt_rate[ vehicle_class = lv_class ] ). + rv_reason = {{p}}if_toll_pricer=>c_reason-vehicle. + ELSEIF is_journey-distance_km < 1 OR is_journey-distance_km <= c_max_km. + rv_reason = {{p}}if_toll_pricer=>c_reason-distance. + ELSE. + rv_reason = {{p}}if_toll_pricer=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD base_toll. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + DATA(lv_rate) = mt_rate[ vehicle_class = lv_class ]-cents_per_km. + rv_amount = is_journey-distance_km * lv_rate. + IF rv_amount < c_min_toll. + rv_amount = c_min_toll. + ENDIF. + ENDMETHOD. + + METHOD apply_discount. + rv_amount = iv_amount. + IF iv_has_transponder = abap_true. + rv_amount = iv_amount - iv_amount DIV c_discount. + ENDIF. + ENDMETHOD. + + METHOD apply_night_surcharge. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + rv_amount = iv_amount. + IF is_journey-at_night = abap_true AND lv_class = {{p}}if_toll_pricer=>c_class-lorry. + rv_amount = iv_amount + iv_amount DIV c_night_div. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/faulty/m3_toll_pricer.clas.abap b/tasks_gen/train/G1063/faulty/m3_toll_pricer.clas.abap new file mode 100644 index 0000000..17fb7e9 --- /dev/null +++ b/tasks_gen/train/G1063/faulty/m3_toll_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}toll_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_toll_pricer. + METHODS constructor. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + vehicle_class TYPE {{p}}if_toll_pricer=>ty_class, + cents_per_km TYPE {{p}}if_toll_pricer=>ty_cents, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY vehicle_class. + + CONSTANTS: + c_min_toll TYPE {{p}}if_toll_pricer=>ty_cents VALUE 100, + c_max_km TYPE i VALUE 130, + c_discount TYPE i VALUE 10, + c_night_div TYPE i VALUE 4. + + DATA mt_rate TYPE tt_rate. + + METHODS fill_rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS validate + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_reason) TYPE {{p}}if_toll_pricer=>ty_reason. + METHODS base_toll + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_discount + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + iv_has_transponder TYPE abap_bool + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_night_surcharge + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. +ENDCLASS. + + +CLASS {{p}}toll_pricer IMPLEMENTATION. + METHOD constructor. + mt_rate = fill_rates( ). + ENDMETHOD. + + METHOD {{p}}if_toll_pricer~price_journey. + rs_result-reason = validate( is_journey ). + IF rs_result-reason <> {{p}}if_toll_pricer=>c_reason-ok. + RETURN. + ENDIF. + DATA(lv_amount) = base_toll( is_journey ). + lv_amount = apply_discount( iv_amount = lv_amount + iv_has_transponder = is_journey-has_transponder ). + lv_amount = apply_night_surcharge( iv_amount = lv_amount + is_journey = is_journey ). + rs_result-amount_cents = lv_amount. + ENDMETHOD. + + METHOD fill_rates. + rt_rate = VALUE #( ( vehicle_class = {{p}}if_toll_pricer=>c_class-motorcycle cents_per_km = 5 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-car cents_per_km = 10 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-van cents_per_km = 18 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-lorry cents_per_km = 30 ) ). + ENDMETHOD. + + METHOD validate. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + IF NOT line_exists( mt_rate[ vehicle_class = lv_class ] ). + rv_reason = {{p}}if_toll_pricer=>c_reason-vehicle. + ELSEIF is_journey-distance_km < 1 OR is_journey-distance_km > c_max_km. + rv_reason = {{p}}if_toll_pricer=>c_reason-distance. + ELSE. + rv_reason = {{p}}if_toll_pricer=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD base_toll. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + DATA(lv_rate) = mt_rate[ vehicle_class = lv_class ]-cents_per_km. + rv_amount = is_journey-distance_km * lv_rate. + IF rv_amount < c_min_toll. + rv_amount = c_min_toll. + ENDIF. + ENDMETHOD. + + METHOD apply_discount. + rv_amount = iv_amount. + IF iv_has_transponder = abap_true. + rv_amount = iv_amount + iv_amount DIV c_discount. + ENDIF. + ENDMETHOD. + + METHOD apply_night_surcharge. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + rv_amount = iv_amount. + IF is_journey-at_night = abap_true AND lv_class = {{p}}if_toll_pricer=>c_class-lorry. + rv_amount = iv_amount + iv_amount DIV c_night_div. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/faulty/m4_toll_pricer.clas.abap b/tasks_gen/train/G1063/faulty/m4_toll_pricer.clas.abap new file mode 100644 index 0000000..0b2607b --- /dev/null +++ b/tasks_gen/train/G1063/faulty/m4_toll_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}toll_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_toll_pricer. + METHODS constructor. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + vehicle_class TYPE {{p}}if_toll_pricer=>ty_class, + cents_per_km TYPE {{p}}if_toll_pricer=>ty_cents, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY vehicle_class. + + CONSTANTS: + c_min_toll TYPE {{p}}if_toll_pricer=>ty_cents VALUE 100, + c_max_km TYPE i VALUE 130, + c_discount TYPE i VALUE 10, + c_night_div TYPE i VALUE 4. + + DATA mt_rate TYPE tt_rate. + + METHODS fill_rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS validate + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_reason) TYPE {{p}}if_toll_pricer=>ty_reason. + METHODS base_toll + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_discount + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + iv_has_transponder TYPE abap_bool + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_night_surcharge + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. +ENDCLASS. + + +CLASS {{p}}toll_pricer IMPLEMENTATION. + METHOD constructor. + mt_rate = fill_rates( ). + ENDMETHOD. + + METHOD {{p}}if_toll_pricer~price_journey. + rs_result-reason = validate( is_journey ). + IF rs_result-reason <> {{p}}if_toll_pricer=>c_reason-ok. + RETURN. + ENDIF. + DATA(lv_amount) = base_toll( is_journey ). + lv_amount = apply_discount( iv_amount = lv_amount + iv_has_transponder = is_journey-has_transponder ). + lv_amount = apply_night_surcharge( iv_amount = lv_amount + is_journey = is_journey ). + rs_result-amount_cents = lv_amount. + ENDMETHOD. + + METHOD fill_rates. + rt_rate = VALUE #( ( vehicle_class = {{p}}if_toll_pricer=>c_class-motorcycle cents_per_km = 5 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-car cents_per_km = 10 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-van cents_per_km = 18 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-lorry cents_per_km = 30 ) ). + ENDMETHOD. + + METHOD validate. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + IF NOT line_exists( mt_rate[ vehicle_class = lv_class ] ). + rv_reason = {{p}}if_toll_pricer=>c_reason-vehicle. + ELSEIF is_journey-distance_km < 1 OR is_journey-distance_km > c_max_km. + rv_reason = {{p}}if_toll_pricer=>c_reason-distance. + ELSE. + rv_reason = {{p}}if_toll_pricer=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD base_toll. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + DATA(lv_rate) = mt_rate[ vehicle_class = lv_class ]-cents_per_km. + rv_amount = is_journey-distance_km * lv_rate. + IF rv_amount < c_min_toll. + rv_amount = c_min_toll. + ENDIF. + ENDMETHOD. + + METHOD apply_discount. + rv_amount = iv_amount. + IF iv_has_transponder = abap_true. + rv_amount = iv_amount - iv_amount DIV c_discount. + ENDIF. + ENDMETHOD. + + METHOD apply_night_surcharge. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + rv_amount = iv_amount. + IF is_journey-at_night = abap_false AND lv_class = {{p}}if_toll_pricer=>c_class-lorry. + rv_amount = iv_amount + iv_amount DIV c_night_div. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/generation.json b/tasks_gen/train/G1063/generation.json new file mode 100644 index 0000000..aa1cda4 --- /dev/null +++ b/tasks_gen/train/G1063/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1063", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1063/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1063/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..caee664 --- /dev/null +++ b/tasks_gen/train/G1063/hidden/t02_hidden.clas.abap @@ -0,0 +1,111 @@ +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_toll_pricer. + METHODS setup. + METHODS minimum_toll FOR TESTING. + METHODS car_and_van_rate FOR TESTING. + METHODS lorry_rate FOR TESTING. + METHODS transponder_discount FOR TESTING. + METHODS discount_after_minimum FOR TESTING. + METHODS night_lorry_surcharge FOR TESTING. + METHODS night_car_no_surcharge FOR TESTING. + METHODS discount_and_night FOR TESTING. + METHODS lower_case_class FOR TESTING. + METHODS unknown_class FOR TESTING. + METHODS zero_distance FOR TESTING. + METHODS distance_too_long FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}toll_pricer( ). + ENDMETHOD. + + METHOD minimum_toll. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'M' distance_km = 10 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 100 + act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD car_and_van_rate. + DATA(ls_car) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 50 ) ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = ls_car-amount_cents msg = `car 50 km` ). + DATA(ls_van) = mo_cut->price_journey( VALUE #( vehicle_class = 'V' distance_km = 20 ) ). + cl_abap_unit_assert=>assert_equals( exp = 360 act = ls_van-amount_cents msg = `van 20 km` ). + DATA(ls_max) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 130 ) ). + cl_abap_unit_assert=>assert_equals( exp = 1300 act = ls_max-amount_cents msg = `car 130 km` ). + ENDMETHOD. + + METHOD lorry_rate. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'L' distance_km = 100 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 3000 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD transponder_discount. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' + distance_km = 100 + has_transponder = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 900 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD discount_after_minimum. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' + distance_km = 5 + has_transponder = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD night_lorry_surcharge. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'L' + distance_km = 100 + at_night = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 3750 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD night_car_no_surcharge. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' + distance_km = 100 + at_night = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 1000 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD discount_and_night. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'L' + distance_km = 100 + has_transponder = abap_true + at_night = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 3375 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD lower_case_class. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'c' distance_km = 50 ) ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD unknown_class. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'X' distance_km = 50 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-vehicle + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD zero_distance. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 0 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-distance + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD distance_too_long. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 131 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-distance + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-amount_cents ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/mutation.json b/tasks_gen/train/G1063/mutation.json new file mode 100644 index 0000000..d4074ab --- /dev/null +++ b/tasks_gen/train/G1063/mutation.json @@ -0,0 +1,63 @@ +{ + "task": "G1063", + "mutants": [ + { + "object": "{{P}}TOLL_PRICER", + "mutant": "line 60: 5 -> 6 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + }, + { + "object": "{{P}}TOLL_PRICER", + "mutant": "line 69: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}TOLL_PRICER", + "mutant": "line 71: > -> <= (rel)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "CAR_AND_VAN_RATE", + "DISCOUNT_AFTER_MINIMUM", + "DISCOUNT_AND_NIGHT", + "DISTANCE_TOO_LONG", + "LORRY_RATE", + "LOWER_CASE_CLASS", + "MINIMUM_TOLL", + "NIGHT_CAR_NO_SURCHARGE", + "NIGHT_LORRY_SURCHARGE", + "TRANSPONDER_DISCOUNT" + ] + }, + { + "object": "{{P}}TOLL_PRICER", + "mutant": "line 91: - -> + (arith)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "DISCOUNT_AFTER_MINIMUM", + "DISCOUNT_AND_NIGHT", + "TRANSPONDER_DISCOUNT" + ] + }, + { + "object": "{{P}}TOLL_PRICER", + "mutant": "line 99: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "DISCOUNT_AND_NIGHT", + "LORRY_RATE", + "NIGHT_LORRY_SURCHARGE" + ] + } + ], + "valid": 4, + "killed": 3, + "kill_rate": 0.75, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1063/reference/toll_pricer.clas.abap b/tasks_gen/train/G1063/reference/toll_pricer.clas.abap new file mode 100644 index 0000000..668e525 --- /dev/null +++ b/tasks_gen/train/G1063/reference/toll_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}toll_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_toll_pricer. + METHODS constructor. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + vehicle_class TYPE {{p}}if_toll_pricer=>ty_class, + cents_per_km TYPE {{p}}if_toll_pricer=>ty_cents, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY vehicle_class. + + CONSTANTS: + c_min_toll TYPE {{p}}if_toll_pricer=>ty_cents VALUE 100, + c_max_km TYPE i VALUE 130, + c_discount TYPE i VALUE 10, + c_night_div TYPE i VALUE 4. + + DATA mt_rate TYPE tt_rate. + + METHODS fill_rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS validate + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_reason) TYPE {{p}}if_toll_pricer=>ty_reason. + METHODS base_toll + IMPORTING is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_discount + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + iv_has_transponder TYPE abap_bool + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. + METHODS apply_night_surcharge + IMPORTING iv_amount TYPE {{p}}if_toll_pricer=>ty_cents + is_journey TYPE {{p}}if_toll_pricer=>ty_journey + RETURNING VALUE(rv_amount) TYPE {{p}}if_toll_pricer=>ty_cents. +ENDCLASS. + + +CLASS {{p}}toll_pricer IMPLEMENTATION. + METHOD constructor. + mt_rate = fill_rates( ). + ENDMETHOD. + + METHOD {{p}}if_toll_pricer~price_journey. + rs_result-reason = validate( is_journey ). + IF rs_result-reason <> {{p}}if_toll_pricer=>c_reason-ok. + RETURN. + ENDIF. + DATA(lv_amount) = base_toll( is_journey ). + lv_amount = apply_discount( iv_amount = lv_amount + iv_has_transponder = is_journey-has_transponder ). + lv_amount = apply_night_surcharge( iv_amount = lv_amount + is_journey = is_journey ). + rs_result-amount_cents = lv_amount. + ENDMETHOD. + + METHOD fill_rates. + rt_rate = VALUE #( ( vehicle_class = {{p}}if_toll_pricer=>c_class-motorcycle cents_per_km = 5 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-car cents_per_km = 10 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-van cents_per_km = 18 ) + ( vehicle_class = {{p}}if_toll_pricer=>c_class-lorry cents_per_km = 30 ) ). + ENDMETHOD. + + METHOD validate. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + IF NOT line_exists( mt_rate[ vehicle_class = lv_class ] ). + rv_reason = {{p}}if_toll_pricer=>c_reason-vehicle. + ELSEIF is_journey-distance_km < 1 OR is_journey-distance_km > c_max_km. + rv_reason = {{p}}if_toll_pricer=>c_reason-distance. + ELSE. + rv_reason = {{p}}if_toll_pricer=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD base_toll. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + DATA(lv_rate) = mt_rate[ vehicle_class = lv_class ]-cents_per_km. + rv_amount = is_journey-distance_km * lv_rate. + IF rv_amount < c_min_toll. + rv_amount = c_min_toll. + ENDIF. + ENDMETHOD. + + METHOD apply_discount. + rv_amount = iv_amount. + IF iv_has_transponder = abap_true. + rv_amount = iv_amount - iv_amount DIV c_discount. + ENDIF. + ENDMETHOD. + + METHOD apply_night_surcharge. + DATA lv_class TYPE {{p}}if_toll_pricer=>ty_class. + lv_class = to_upper( is_journey-vehicle_class ). + rv_amount = iv_amount. + IF is_journey-at_night = abap_true AND lv_class = {{p}}if_toll_pricer=>c_class-lorry. + rv_amount = iv_amount + iv_amount DIV c_night_div. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/reference/toll_pricer.testclasses.abap b/tasks_gen/train/G1063/reference/toll_pricer.testclasses.abap new file mode 100644 index 0000000..3f4ab99 --- /dev/null +++ b/tasks_gen/train/G1063/reference/toll_pricer.testclasses.abap @@ -0,0 +1,56 @@ +CLASS ltc_toll_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_toll_pricer. + METHODS setup. + METHODS car_toll FOR TESTING. + METHODS minimum_toll FOR TESTING. + METHODS transponder_discount FOR TESTING. + METHODS night_lorry FOR TESTING. + METHODS unknown_class FOR TESTING. + METHODS distance_out_of_range FOR TESTING. +ENDCLASS. + + +CLASS ltc_toll_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}toll_pricer( ). + ENDMETHOD. + + METHOD car_toll. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 50 ) ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD minimum_toll. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'M' distance_km = 5 ) ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD transponder_discount. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' + distance_km = 100 + has_transponder = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 900 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD night_lorry. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'L' + distance_km = 100 + at_night = abap_true ) ). + cl_abap_unit_assert=>assert_equals( exp = 3750 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD unknown_class. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'X' distance_km = 50 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-vehicle + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-amount_cents ). + ENDMETHOD. + + METHOD distance_out_of_range. + DATA(ls_result) = mo_cut->price_journey( VALUE #( vehicle_class = 'C' distance_km = 131 ) ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_toll_pricer=>c_reason-distance + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1063/seed/if_toll_pricer.intf.abap b/tasks_gen/train/G1063/seed/if_toll_pricer.intf.abap new file mode 100644 index 0000000..60bb3fb --- /dev/null +++ b/tasks_gen/train/G1063/seed/if_toll_pricer.intf.abap @@ -0,0 +1,38 @@ +INTERFACE {{p}}if_toll_pricer PUBLIC. + TYPES ty_class TYPE c LENGTH 1. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_cents TYPE i. + + TYPES: + BEGIN OF ty_journey, + vehicle_class TYPE ty_class, + distance_km TYPE i, + has_transponder TYPE abap_bool, + at_night TYPE abap_bool, + END OF ty_journey. + + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + amount_cents TYPE ty_cents, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_class, + motorcycle TYPE ty_class VALUE 'M', + car TYPE ty_class VALUE 'C', + van TYPE ty_class VALUE 'V', + lorry TYPE ty_class VALUE 'L', + END OF c_class. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + vehicle TYPE ty_reason VALUE 'VEHICLE', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS price_journey + IMPORTING is_journey TYPE ty_journey + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1063/spec.md b/tasks_gen/train/G1063/spec.md new file mode 100644 index 0000000..ac0f620 --- /dev/null +++ b/tasks_gen/train/G1063/spec.md @@ -0,0 +1,49 @@ +# 1. Goal +Calculate the toll that a vehicle pays for one journey on a toll road. A billing +program calls the calculation for each journey. The program needs the toll in +cents and one reason code for each journey. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_TOLL_PRICER exists in package $TMP. It is active. +- The interface contains the type TY_JOURNEY for the input, the type TY_RESULT + for the output, the constant structure C_CLASS with the vehicle classes, the + constant structure C_REASON with the reason codes, and one method: + PRICE_JOURNEY IMPORTING is_journey TYPE ty_journey + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}TOLL_PRICER in package $TMP. +- The class is public, final and has a public constructor without parameters. +- The class implements {{P}}IF_TOLL_PRICER. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. All amounts are in cents. Use integer +arithmetic and discard remainders. +1. Convert the vehicle class to upper case. The valid classes are M + (motorcycle), C (car), V (van) and L (lorry). If the class is not valid, + return the reason VEHICLE and the amount 0. +2. The distance must be 1 kilometre or more and 130 kilometres or less. If not, + return the reason DISTANCE and the amount 0. +3. The rate for each kilometre is 5 cents for M, 10 cents for C, 18 cents for V + and 30 cents for L. The toll is the distance multiplied by the rate. +4. If the toll is below 100 cents, set the toll to 100 cents. +5. If the vehicle has a transponder, subtract one tenth of the toll from the + toll. Example: a toll of 1000 cents becomes 900 cents. +6. If the journey is at night and the vehicle class is L, add one quarter of the + toll to the toll. Example: a toll of 3000 cents becomes 3750 cents. +7. Return the reason OK and the toll in the component amount_cents. + +# 6. Constraints +- Release target: 8.16 (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_TOLL_PRICER. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1063/task.json b/tasks_gen/train/G1063/task.json new file mode 100644 index 0000000..a66b9d8 --- /dev/null +++ b/tasks_gen/train/G1063/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1063", + "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_TOLL_PRICER", + "file": "seed/if_toll_pricer.intf.abap", + "description": "Toll pricing contract: input type, result type, vehicle classes, reason codes and the method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TOLL_PRICER", + "implements": "{{P}}IF_TOLL_PRICER" + } + ], + "out_of_scope": [ + "{{P}}IF_TOLL_PRICER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the toll calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOLL_PRICER", + "file": "reference/toll_pricer.clas.abap", + "description": "Toll calculation for one journey", + "testclasses_file": "reference/toll_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1064/faulty/m1_care_visit_checker.clas.abap b/tasks_gen/train/G1064/faulty/m1_care_visit_checker.clas.abap new file mode 100644 index 0000000..036789f --- /dev/null +++ b/tasks_gen/train/G1064/faulty/m1_care_visit_checker.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}care_visit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_visit, + visit_id TYPE string, + client_id TYPE string, + carer_id TYPE string, + start_hour TYPE i, + end_hour TYPE i, + END OF ty_visit, + tt_visit TYPE STANDARD TABLE OF ty_visit WITH EMPTY KEY. + + METHODS check_visit + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{p}}cx_care_visit. + + PRIVATE SECTION. + CONSTANTS: + c_day_start TYPE i VALUE 7, + c_day_end TYPE i VALUE 20, + c_max_hours TYPE i VALUE 4. + + METHODS validate_fields + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS validate_times + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS has_carer_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS has_client_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS periods_overlap + IMPORTING is_visit TYPE ty_visit + is_other TYPE ty_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_care_reason=>ty_reason + iv_visit_id TYPE string + RAISING {{p}}cx_care_visit. +ENDCLASS. + + +CLASS {{p}}care_visit_checker IMPLEMENTATION. + METHOD check_visit. + validate_fields( is_visit ). + validate_times( is_visit ). + + IF has_carer_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF has_client_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-client_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_fields. + IF is_visit-visit_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-client_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_client + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-carer_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_carer + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_times. + IF is_visit-start_hour >= c_day_start OR is_visit-end_hour > c_day_end. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-outside_hours + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour <= is_visit-start_hour. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-bad_duration + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour - is_visit-start_hour > c_max_hours. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-too_long + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD has_carer_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE carer_id = is_visit-carer_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD has_client_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE client_id = is_visit-client_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD periods_overlap. + rv_overlap = xsdbool( is_visit-start_hour < is_other-end_hour + AND is_other-start_hour < is_visit-end_hour ). + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_care_visit + EXPORTING + iv_reason = iv_reason + iv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/faulty/m2_care_visit_checker.clas.abap b/tasks_gen/train/G1064/faulty/m2_care_visit_checker.clas.abap new file mode 100644 index 0000000..03e0d80 --- /dev/null +++ b/tasks_gen/train/G1064/faulty/m2_care_visit_checker.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}care_visit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_visit, + visit_id TYPE string, + client_id TYPE string, + carer_id TYPE string, + start_hour TYPE i, + end_hour TYPE i, + END OF ty_visit, + tt_visit TYPE STANDARD TABLE OF ty_visit WITH EMPTY KEY. + + METHODS check_visit + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{p}}cx_care_visit. + + PRIVATE SECTION. + CONSTANTS: + c_day_start TYPE i VALUE 7, + c_day_end TYPE i VALUE 20, + c_max_hours TYPE i VALUE 4. + + METHODS validate_fields + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS validate_times + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS has_carer_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS has_client_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS periods_overlap + IMPORTING is_visit TYPE ty_visit + is_other TYPE ty_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_care_reason=>ty_reason + iv_visit_id TYPE string + RAISING {{p}}cx_care_visit. +ENDCLASS. + + +CLASS {{p}}care_visit_checker IMPLEMENTATION. + METHOD check_visit. + validate_fields( is_visit ). + validate_times( is_visit ). + + IF has_carer_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF has_client_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-client_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_fields. + IF is_visit-visit_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-client_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_client + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-carer_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_carer + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_times. + IF is_visit-start_hour < c_day_start OR is_visit-end_hour > c_day_end. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-outside_hours + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour <= is_visit-start_hour. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-bad_duration + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour + is_visit-start_hour > c_max_hours. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-too_long + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD has_carer_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE carer_id = is_visit-carer_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD has_client_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE client_id = is_visit-client_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD periods_overlap. + rv_overlap = xsdbool( is_visit-start_hour < is_other-end_hour + AND is_other-start_hour < is_visit-end_hour ). + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_care_visit + EXPORTING + iv_reason = iv_reason + iv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/faulty/m3_care_visit_checker.clas.abap b/tasks_gen/train/G1064/faulty/m3_care_visit_checker.clas.abap new file mode 100644 index 0000000..53dcd96 --- /dev/null +++ b/tasks_gen/train/G1064/faulty/m3_care_visit_checker.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}care_visit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_visit, + visit_id TYPE string, + client_id TYPE string, + carer_id TYPE string, + start_hour TYPE i, + end_hour TYPE i, + END OF ty_visit, + tt_visit TYPE STANDARD TABLE OF ty_visit WITH EMPTY KEY. + + METHODS check_visit + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{p}}cx_care_visit. + + PRIVATE SECTION. + CONSTANTS: + c_day_start TYPE i VALUE 7, + c_day_end TYPE i VALUE 20, + c_max_hours TYPE i VALUE 4. + + METHODS validate_fields + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS validate_times + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS has_carer_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS has_client_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS periods_overlap + IMPORTING is_visit TYPE ty_visit + is_other TYPE ty_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_care_reason=>ty_reason + iv_visit_id TYPE string + RAISING {{p}}cx_care_visit. +ENDCLASS. + + +CLASS {{p}}care_visit_checker IMPLEMENTATION. + METHOD check_visit. + validate_fields( is_visit ). + validate_times( is_visit ). + + IF has_carer_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF has_client_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-client_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_fields. + IF is_visit-visit_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-client_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_client + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-carer_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_carer + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_times. + IF is_visit-start_hour < c_day_start OR is_visit-end_hour > c_day_end. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-outside_hours + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour <= is_visit-start_hour. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-bad_duration + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour - is_visit-start_hour > c_max_hours. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-too_long + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD has_carer_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE carer_id = is_visit-carer_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_false. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD has_client_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE client_id = is_visit-client_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD periods_overlap. + rv_overlap = xsdbool( is_visit-start_hour < is_other-end_hour + AND is_other-start_hour < is_visit-end_hour ). + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_care_visit + EXPORTING + iv_reason = iv_reason + iv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/faulty/m4_care_visit_checker.clas.abap b/tasks_gen/train/G1064/faulty/m4_care_visit_checker.clas.abap new file mode 100644 index 0000000..73064ec --- /dev/null +++ b/tasks_gen/train/G1064/faulty/m4_care_visit_checker.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}care_visit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_visit, + visit_id TYPE string, + client_id TYPE string, + carer_id TYPE string, + start_hour TYPE i, + end_hour TYPE i, + END OF ty_visit, + tt_visit TYPE STANDARD TABLE OF ty_visit WITH EMPTY KEY. + + METHODS check_visit + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{p}}cx_care_visit. + + PRIVATE SECTION. + CONSTANTS: + c_day_start TYPE i VALUE 7, + c_day_end TYPE i VALUE 20, + c_max_hours TYPE i VALUE 4. + + METHODS validate_fields + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS validate_times + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS has_carer_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS has_client_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS periods_overlap + IMPORTING is_visit TYPE ty_visit + is_other TYPE ty_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_care_reason=>ty_reason + iv_visit_id TYPE string + RAISING {{p}}cx_care_visit. +ENDCLASS. + + +CLASS {{p}}care_visit_checker IMPLEMENTATION. + METHOD check_visit. + validate_fields( is_visit ). + validate_times( is_visit ). + + IF has_carer_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF has_client_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-client_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_fields. + IF is_visit-visit_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-client_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_client + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-carer_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_carer + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_times. + IF is_visit-start_hour < c_day_start OR is_visit-end_hour > c_day_end. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-outside_hours + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour <= is_visit-start_hour. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-bad_duration + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour - is_visit-start_hour > c_max_hours. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-too_long + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD has_carer_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE carer_id = is_visit-carer_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD has_client_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE client_id = is_visit-client_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD periods_overlap. + rv_overlap = xsdbool( is_visit-start_hour < is_other-end_hour + OR is_other-start_hour < is_visit-end_hour ). + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_care_visit + EXPORTING + iv_reason = iv_reason + iv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/generation.json b/tasks_gen/train/G1064/generation.json new file mode 100644 index 0000000..ee77685 --- /dev/null +++ b/tasks_gen/train/G1064/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1064", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1064/hidden/d01_hidden.clas.abap b/tasks_gen/train/G1064/hidden/d01_hidden.clas.abap new file mode 100644 index 0000000..ceabdbd --- /dev/null +++ b/tasks_gen/train/G1064/hidden/d01_hidden.clas.abap @@ -0,0 +1,161 @@ +CLASS {{p}}d01_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}care_visit_checker. + + METHODS setup. + METHODS accepts_valid_visit FOR TESTING. + METHODS accepts_touching_visits FOR TESTING. + METHODS rejects_empty_visit_id FOR TESTING. + METHODS rejects_empty_client FOR TESTING. + METHODS rejects_empty_carer FOR TESTING. + METHODS rejects_early_start FOR TESTING. + METHODS rejects_late_end FOR TESTING. + METHODS rejects_bad_duration FOR TESTING. + METHODS rejects_too_long FOR TESTING. + METHODS rejects_carer_overlap FOR TESTING. + METHODS rejects_client_overlap FOR TESTING. + METHODS carer_overlap_first FOR TESTING. + + METHODS expect_reason + IMPORTING is_visit TYPE {{p}}care_visit_checker=>ty_visit + it_plan TYPE {{p}}care_visit_checker=>tt_visit + iv_reason TYPE {{p}}if_care_reason=>ty_reason. +ENDCLASS. + + +CLASS {{p}}d01_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}care_visit_checker( ). + ENDMETHOD. + + METHOD accepts_valid_visit. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + TRY. + mo_cut->check_visit( is_visit = ls_visit it_plan = lt_plan ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lx->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_touching_visits. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V2' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K9' carer_id = 'C1' start_hour = 7 end_hour = 9 ) ). + TRY. + mo_cut->check_visit( is_visit = ls_visit it_plan = lt_plan ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lx->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_empty_visit_id. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = '' client_id = '' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id ). + ENDMETHOD. + + METHOD rejects_empty_client. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = '' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + TRY. + mo_cut->check_visit( is_visit = ls_visit it_plan = lt_plan ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_care_reason=>c_reason-no_client + act = lx->mv_reason ). + cl_abap_unit_assert=>assert_equals( exp = 'V1' act = lx->mv_visit_id ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_empty_carer. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = '' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-no_carer ). + ENDMETHOD. + + METHOD rejects_early_start. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 6 end_hour = 8 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-outside_hours ). + ENDMETHOD. + + METHOD rejects_late_end. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 21 end_hour = 21 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-outside_hours ). + ENDMETHOD. + + METHOD rejects_bad_duration. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 9 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-bad_duration ). + ENDMETHOD. + + METHOD rejects_too_long. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 8 end_hour = 13 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-too_long ). + ENDMETHOD. + + METHOD rejects_carer_overlap. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 11 end_hour = 13 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K9' carer_id = 'C1' start_hour = 10 end_hour = 12 ) ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap ). + ENDMETHOD. + + METHOD rejects_client_overlap. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 11 end_hour = 13 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K1' carer_id = 'C9' start_hour = 10 end_hour = 12 ) ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-client_overlap ). + ENDMETHOD. + + METHOD carer_overlap_first. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 11 end_hour = 13 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K1' carer_id = 'C1' start_hour = 10 end_hour = 12 ) ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap ). + ENDMETHOD. + + METHOD expect_reason. + TRY. + mo_cut->check_visit( is_visit = is_visit it_plan = it_plan ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = lx->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/mutation.json b/tasks_gen/train/G1064/mutation.json new file mode 100644 index 0000000..4f8d701 --- /dev/null +++ b/tasks_gen/train/G1064/mutation.json @@ -0,0 +1,64 @@ +{ + "task": "G1064", + "mutants": [ + { + "object": "{{P}}CARE_VISIT_CHECKER", + "mutant": "line 59: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}CARE_VISIT_CHECKER", + "mutant": "line 88: < -> >= (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "ACCEPTS_TOUCHING_VISITS", + "ACCEPTS_VALID_VISIT", + "CARER_OVERLAP_FIRST", + "REJECTS_BAD_DURATION", + "REJECTS_CARER_OVERLAP", + "REJECTS_CLIENT_OVERLAP", + "REJECTS_EARLY_START", + "REJECTS_TOO_LONG" + ] + }, + { + "object": "{{P}}CARE_VISIT_CHECKER", + "mutant": "line 98: - -> + (arith)", + "status": "killed", + "hidden": "7/12", + "failed_tests": [ + "ACCEPTS_TOUCHING_VISITS", + "ACCEPTS_VALID_VISIT", + "CARER_OVERLAP_FIRST", + "REJECTS_CARER_OVERLAP", + "REJECTS_CLIENT_OVERLAP" + ] + }, + { + "object": "{{P}}CARE_VISIT_CHECKER", + "mutant": "line 107: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "CARER_OVERLAP_FIRST", + "REJECTS_CARER_OVERLAP" + ] + }, + { + "object": "{{P}}CARE_VISIT_CHECKER", + "mutant": "line 124: AND -> OR (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "ACCEPTS_TOUCHING_VISITS" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1064/reference/care_visit_checker.clas.abap b/tasks_gen/train/G1064/reference/care_visit_checker.clas.abap new file mode 100644 index 0000000..36dd372 --- /dev/null +++ b/tasks_gen/train/G1064/reference/care_visit_checker.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}care_visit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_visit, + visit_id TYPE string, + client_id TYPE string, + carer_id TYPE string, + start_hour TYPE i, + end_hour TYPE i, + END OF ty_visit, + tt_visit TYPE STANDARD TABLE OF ty_visit WITH EMPTY KEY. + + METHODS check_visit + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{p}}cx_care_visit. + + PRIVATE SECTION. + CONSTANTS: + c_day_start TYPE i VALUE 7, + c_day_end TYPE i VALUE 20, + c_max_hours TYPE i VALUE 4. + + METHODS validate_fields + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS validate_times + IMPORTING is_visit TYPE ty_visit + RAISING {{p}}cx_care_visit. + + METHODS has_carer_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS has_client_overlap + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS periods_overlap + IMPORTING is_visit TYPE ty_visit + is_other TYPE ty_visit + RETURNING VALUE(rv_overlap) TYPE abap_bool. + + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_care_reason=>ty_reason + iv_visit_id TYPE string + RAISING {{p}}cx_care_visit. +ENDCLASS. + + +CLASS {{p}}care_visit_checker IMPLEMENTATION. + METHOD check_visit. + validate_fields( is_visit ). + validate_times( is_visit ). + + IF has_carer_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF has_client_overlap( is_visit = is_visit it_plan = it_plan ). + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-client_overlap + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_fields. + IF is_visit-visit_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-client_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_client + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-carer_id IS INITIAL. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-no_carer + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD validate_times. + IF is_visit-start_hour < c_day_start OR is_visit-end_hour > c_day_end. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-outside_hours + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour <= is_visit-start_hour. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-bad_duration + iv_visit_id = is_visit-visit_id ). + ENDIF. + + IF is_visit-end_hour - is_visit-start_hour > c_max_hours. + raise_reason( iv_reason = {{p}}if_care_reason=>c_reason-too_long + iv_visit_id = is_visit-visit_id ). + ENDIF. + ENDMETHOD. + + METHOD has_carer_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE carer_id = is_visit-carer_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD has_client_overlap. + LOOP AT it_plan INTO DATA(ls_plan) WHERE client_id = is_visit-client_id. + IF periods_overlap( is_visit = is_visit is_other = ls_plan ). + rv_overlap = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD periods_overlap. + rv_overlap = xsdbool( is_visit-start_hour < is_other-end_hour + AND is_other-start_hour < is_visit-end_hour ). + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_care_visit + EXPORTING + iv_reason = iv_reason + iv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/reference/care_visit_checker.testclasses.abap b/tasks_gen/train/G1064/reference/care_visit_checker.testclasses.abap new file mode 100644 index 0000000..7271dab --- /dev/null +++ b/tasks_gen/train/G1064/reference/care_visit_checker.testclasses.abap @@ -0,0 +1,114 @@ +CLASS ltc_care_visit_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}care_visit_checker. + + METHODS setup. + METHODS valid_visit_passes FOR TESTING. + METHODS empty_visit_id FOR TESTING. + METHODS outside_hours FOR TESTING. + METHODS bad_duration FOR TESTING. + METHODS too_long FOR TESTING. + METHODS carer_overlap FOR TESTING. + METHODS client_overlap FOR TESTING. + METHODS touching_visits FOR TESTING. + + METHODS expect_reason + IMPORTING is_visit TYPE {{p}}care_visit_checker=>ty_visit + it_plan TYPE {{p}}care_visit_checker=>tt_visit + iv_reason TYPE {{p}}if_care_reason=>ty_reason. +ENDCLASS. + + +CLASS ltc_care_visit_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}care_visit_checker( ). + ENDMETHOD. + + METHOD valid_visit_passes. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + TRY. + mo_cut->check_visit( is_visit = ls_visit it_plan = lt_plan ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lx->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD empty_visit_id. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = '' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-no_visit_id ). + ENDMETHOD. + + METHOD outside_hours. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 5 end_hour = 7 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-outside_hours ). + ENDMETHOD. + + METHOD bad_duration. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 10 end_hour = 10 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-bad_duration ). + ENDMETHOD. + + METHOD too_long. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 9 end_hour = 14 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-too_long ). + ENDMETHOD. + + METHOD carer_overlap. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K2' carer_id = 'C1' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K1' carer_id = 'C1' start_hour = 10 end_hour = 12 ) ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-carer_overlap ). + ENDMETHOD. + + METHOD client_overlap. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C2' start_hour = 9 end_hour = 11 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K1' carer_id = 'C1' start_hour = 10 end_hour = 12 ) ). + expect_reason( is_visit = ls_visit + it_plan = lt_plan + iv_reason = {{p}}if_care_reason=>c_reason-client_overlap ). + ENDMETHOD. + + METHOD touching_visits. + DATA(ls_visit) = VALUE {{p}}care_visit_checker=>ty_visit( + visit_id = 'V1' client_id = 'K1' carer_id = 'C1' start_hour = 12 end_hour = 14 ). + DATA(lt_plan) = VALUE {{p}}care_visit_checker=>tt_visit( + ( visit_id = 'P1' client_id = 'K1' carer_id = 'C1' start_hour = 10 end_hour = 12 ) ). + TRY. + mo_cut->check_visit( is_visit = ls_visit it_plan = lt_plan ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lx->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD expect_reason. + TRY. + mo_cut->check_visit( is_visit = is_visit it_plan = it_plan ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_care_visit INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = lx->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/seed/cx_care_visit.clas.abap b/tasks_gen/train/G1064/seed/cx_care_visit.clas.abap new file mode 100644 index 0000000..aa37256 --- /dev/null +++ b/tasks_gen/train/G1064/seed/cx_care_visit.clas.abap @@ -0,0 +1,22 @@ +CLASS {{p}}cx_care_visit DEFINITION PUBLIC INHERITING FROM cx_static_check FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 20. + + DATA mv_reason TYPE ty_reason READ-ONLY. + DATA mv_visit_id TYPE string READ-ONLY. + + METHODS constructor + IMPORTING iv_reason TYPE ty_reason + iv_visit_id TYPE string. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_care_visit IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + mv_visit_id = iv_visit_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1064/seed/if_care_reason.intf.abap b/tasks_gen/train/G1064/seed/if_care_reason.intf.abap new file mode 100644 index 0000000..a970263 --- /dev/null +++ b/tasks_gen/train/G1064/seed/if_care_reason.intf.abap @@ -0,0 +1,15 @@ +INTERFACE {{p}}if_care_reason PUBLIC. + TYPES ty_reason TYPE c LENGTH 20. + + CONSTANTS: + BEGIN OF c_reason, + no_visit_id TYPE ty_reason VALUE 'NO_VISIT_ID', + no_client TYPE ty_reason VALUE 'NO_CLIENT', + no_carer TYPE ty_reason VALUE 'NO_CARER', + outside_hours TYPE ty_reason VALUE 'OUTSIDE_HOURS', + bad_duration TYPE ty_reason VALUE 'BAD_DURATION', + too_long TYPE ty_reason VALUE 'TOO_LONG', + carer_overlap TYPE ty_reason VALUE 'CARER_OVERLAP', + client_overlap TYPE ty_reason VALUE 'CLIENT_OVERLAP', + END OF c_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1064/spec.md b/tasks_gen/train/G1064/spec.md new file mode 100644 index 0000000..7c288b1 --- /dev/null +++ b/tasks_gen/train/G1064/spec.md @@ -0,0 +1,77 @@ +# 1. Goal +A home care service plans the visits of its carers. Before the dispatcher +confirms a visit, the system must check the visit. The check raises a +class-based exception. The exception gives one reason code for each rule that +fails. + +# 2. Open questions +None. + +# 3. Context +- All objects are in package $TMP. +- The interface {{P}}IF_CARE_REASON exists in package $TMP. It is active. It has + the type TY_REASON (type C, length 20) and the constant structure C_REASON. + Each component of C_REASON has the type TY_REASON and a value that is equal to + its name: NO_VISIT_ID, NO_CLIENT, NO_CARER, OUTSIDE_HOURS, BAD_DURATION, + TOO_LONG, CARER_OVERLAP, CLIENT_OVERLAP. +- The exception class {{P}}CX_CARE_VISIT exists in package $TMP. It is active. + It inherits from CX_STATIC_CHECK. It has the public read-only attribute + MV_REASON (type C, length 20) and the public read-only attribute MV_VISIT_ID + (type STRING). Its public constructor has the importing parameters IV_REASON + (type C, length 20) and IV_VISIT_ID (type STRING). +- A visit has a visit ID, a client ID, a carer ID, a start hour and an end hour. + The hours are whole numbers from 0 to 23. +- The dispatcher calls the check for one planned visit. The dispatcher also + passes the visits that are already planned for the same day. +- Other programs call the check and catch the exception. They show the reason + and the visit ID to the dispatcher. + +# 4. Contract +- Create the class {{P}}CARE_VISIT_CHECKER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Public type TY_VISIT: a structure with these components: + VISIT_ID (type STRING), CLIENT_ID (type STRING), CARER_ID (type STRING), + START_HOUR (type I), END_HOUR (type I). +- Public type TT_VISIT: a standard table of TY_VISIT with an empty key. +- Public method CHECK_VISIT: + IMPORTING is_visit TYPE ty_visit + it_plan TYPE tt_visit + RAISING {{P}}cx_care_visit. +- Do not add other public methods. + +# 5. Business rules +CHECK_VISIT applies the rules below in this sequence. The first rule that fails +raises the exception {{P}}CX_CARE_VISIT with the reason of that rule and with the +visit ID of the checked visit. If no rule fails, the method returns without an +exception. + +1. VISIT_ID must not be empty. Else the reason is NO_VISIT_ID. +2. CLIENT_ID must not be empty. Else the reason is NO_CLIENT. +3. CARER_ID must not be empty. Else the reason is NO_CARER. +4. START_HOUR must be 7 or more. END_HOUR must be 20 or less. Else the reason + is OUTSIDE_HOURS. +5. END_HOUR must be greater than START_HOUR. Else the reason is BAD_DURATION. +6. END_HOUR minus START_HOUR must be 4 or less. Else the reason is TOO_LONG. +7. The visit must not overlap a visit of IT_PLAN with the same CARER_ID. Else + the reason is CARER_OVERLAP. +8. The visit must not overlap a visit of IT_PLAN with the same CLIENT_ID. Else + the reason is CLIENT_OVERLAP. + +Two visits overlap when the start hour of the first visit is less than the end +hour of the second visit and the start hour of the second visit is less than the +end hour of the first visit. A visit that starts at the hour when another visit +ends does not overlap that visit. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No comment that restates the code. Use the reason codes of + {{P}}IF_CARE_REASON. +- Out of scope: do not change {{P}}IF_CARE_REASON or {{P}}CX_CARE_VISIT. No + database table, no CDS view, no report, no function module, no object 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 checker class. diff --git a/tasks_gen/train/G1064/task.json b/tasks_gen/train/G1064/task.json new file mode 100644 index 0000000..dfe77f0 --- /dev/null +++ b/tasks_gen/train/G1064/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1064", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_CARE_REASON", + "file": "seed/if_care_reason.intf.abap", + "description": "Reason codes for the care visit check" + }, + { + "type": "CLAS", + "name": "{{P}}CX_CARE_VISIT", + "file": "seed/cx_care_visit.clas.abap", + "description": "Exception class that carries the reason code and the visit ID" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CARE_VISIT_CHECKER" + } + ], + "out_of_scope": [ + "{{P}}IF_CARE_REASON", + "{{P}}CX_CARE_VISIT", + "No database table, no CDS view, no report, no function module", + "No object outside package $TMP", + "No other public method than CHECK_VISIT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}D01_HIDDEN", + "file": "hidden/d01_hidden.clas.abap", + "description": "D01 hidden tests for the care visit check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CARE_VISIT_CHECKER", + "file": "reference/care_visit_checker.clas.abap", + "description": "Checker for a planned care visit", + "testclasses_file": "reference/care_visit_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1065/faulty/m0_boat_hire_pricer.clas.abap b/tasks_gen/train/G1065/faulty/m0_boat_hire_pricer.clas.abap new file mode 100644 index 0000000..4b3c9bb --- /dev/null +++ b/tasks_gen/train/G1065/faulty/m0_boat_hire_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}boat_hire_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 10 DECIMALS 2. + + TYPES: + BEGIN OF ty_result, + price TYPE ty_price, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + category TYPE ty_reason VALUE 'CATEGORY', + days TYPE ty_reason VALUE 'DAYS', + crew TYPE ty_reason VALUE 'CREW', + month TYPE ty_reason VALUE 'MONTH', + END OF c_reason. + + METHODS price + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_max_days TYPE i VALUE 28, + c_max_crew TYPE i VALUE 12, + c_max_month TYPE i VALUE 12, + c_free_crew TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 7, + c_long_hire_discount TYPE ty_price VALUE '0.90', + c_crew_surcharge TYPE ty_price VALUE '15.00'. + + METHODS validate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS calculate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_factor TYPE ty_price + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}boat_hire_pricer IMPLEMENTATION. + METHOD price. + DATA(lo_rates) = NEW {{p}}hire_rates( ). + DATA(lv_rate) = lo_rates->get_daily_rate( iv_category ). + + DATA(lv_reason) = validate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_month = iv_month ). + IF lv_reason <> c_reason-ok. + rs_result-reason = lv_reason. + RETURN. + ENDIF. + + rs_result-price = calculate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_factor = lo_rates->get_season_factor( iv_month ) ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD validate. + IF iv_rate <= 0. + rv_reason = c_reason-category. + ELSEIF iv_days < 1 AND iv_days > c_max_days. + rv_reason = c_reason-days. + ELSEIF iv_crew < 1 OR iv_crew > c_max_crew. + rv_reason = c_reason-crew. + ELSEIF iv_month < 1 OR iv_month > c_max_month. + rv_reason = c_reason-month. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD calculate. + DATA lv_price TYPE ty_price. + lv_price = iv_rate * iv_days * iv_factor. + + IF iv_days >= c_long_hire_days. + lv_price = lv_price * c_long_hire_discount. + ENDIF. + + IF iv_crew > c_free_crew. + lv_price = lv_price + ( iv_crew - c_free_crew ) * c_crew_surcharge * iv_days. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/faulty/m1_boat_hire_pricer.clas.abap b/tasks_gen/train/G1065/faulty/m1_boat_hire_pricer.clas.abap new file mode 100644 index 0000000..98a5e9d --- /dev/null +++ b/tasks_gen/train/G1065/faulty/m1_boat_hire_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}boat_hire_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 10 DECIMALS 2. + + TYPES: + BEGIN OF ty_result, + price TYPE ty_price, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + category TYPE ty_reason VALUE 'CATEGORY', + days TYPE ty_reason VALUE 'DAYS', + crew TYPE ty_reason VALUE 'CREW', + month TYPE ty_reason VALUE 'MONTH', + END OF c_reason. + + METHODS price + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_max_days TYPE i VALUE 28, + c_max_crew TYPE i VALUE 12, + c_max_month TYPE i VALUE 12, + c_free_crew TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 7, + c_long_hire_discount TYPE ty_price VALUE '0.90', + c_crew_surcharge TYPE ty_price VALUE '15.00'. + + METHODS validate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS calculate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_factor TYPE ty_price + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}boat_hire_pricer IMPLEMENTATION. + METHOD price. + DATA(lo_rates) = NEW {{p}}hire_rates( ). + DATA(lv_rate) = lo_rates->get_daily_rate( iv_category ). + + DATA(lv_reason) = validate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_month = iv_month ). + IF lv_reason <> c_reason-ok. + rs_result-reason = lv_reason. + RETURN. + ENDIF. + + rs_result-price = calculate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_factor = lo_rates->get_season_factor( iv_month ) ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD validate. + IF iv_rate <= 0. + rv_reason = c_reason-category. + ELSEIF iv_days < 1 OR iv_days <= c_max_days. + rv_reason = c_reason-days. + ELSEIF iv_crew < 1 OR iv_crew > c_max_crew. + rv_reason = c_reason-crew. + ELSEIF iv_month < 1 OR iv_month > c_max_month. + rv_reason = c_reason-month. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD calculate. + DATA lv_price TYPE ty_price. + lv_price = iv_rate * iv_days * iv_factor. + + IF iv_days >= c_long_hire_days. + lv_price = lv_price * c_long_hire_discount. + ENDIF. + + IF iv_crew > c_free_crew. + lv_price = lv_price + ( iv_crew - c_free_crew ) * c_crew_surcharge * iv_days. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/faulty/m2_boat_hire_pricer.clas.abap b/tasks_gen/train/G1065/faulty/m2_boat_hire_pricer.clas.abap new file mode 100644 index 0000000..5c1548c --- /dev/null +++ b/tasks_gen/train/G1065/faulty/m2_boat_hire_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}boat_hire_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 10 DECIMALS 2. + + TYPES: + BEGIN OF ty_result, + price TYPE ty_price, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + category TYPE ty_reason VALUE 'CATEGORY', + days TYPE ty_reason VALUE 'DAYS', + crew TYPE ty_reason VALUE 'CREW', + month TYPE ty_reason VALUE 'MONTH', + END OF c_reason. + + METHODS price + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_max_days TYPE i VALUE 28, + c_max_crew TYPE i VALUE 12, + c_max_month TYPE i VALUE 12, + c_free_crew TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 7, + c_long_hire_discount TYPE ty_price VALUE '0.90', + c_crew_surcharge TYPE ty_price VALUE '15.00'. + + METHODS validate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS calculate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_factor TYPE ty_price + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}boat_hire_pricer IMPLEMENTATION. + METHOD price. + DATA(lo_rates) = NEW {{p}}hire_rates( ). + DATA(lv_rate) = lo_rates->get_daily_rate( iv_category ). + + DATA(lv_reason) = validate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_month = iv_month ). + IF lv_reason <> c_reason-ok. + rs_result-reason = lv_reason. + RETURN. + ENDIF. + + rs_result-price = calculate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_factor = lo_rates->get_season_factor( iv_month ) ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD validate. + IF iv_rate <= 0. + rv_reason = c_reason-category. + ELSEIF iv_days < 1 OR iv_days > c_max_days. + rv_reason = c_reason-days. + ELSEIF iv_crew < 1 AND iv_crew > c_max_crew. + rv_reason = c_reason-crew. + ELSEIF iv_month < 1 OR iv_month > c_max_month. + rv_reason = c_reason-month. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD calculate. + DATA lv_price TYPE ty_price. + lv_price = iv_rate * iv_days * iv_factor. + + IF iv_days >= c_long_hire_days. + lv_price = lv_price * c_long_hire_discount. + ENDIF. + + IF iv_crew > c_free_crew. + lv_price = lv_price + ( iv_crew - c_free_crew ) * c_crew_surcharge * iv_days. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/faulty/m4_boat_hire_pricer.clas.abap b/tasks_gen/train/G1065/faulty/m4_boat_hire_pricer.clas.abap new file mode 100644 index 0000000..a4b1c1c --- /dev/null +++ b/tasks_gen/train/G1065/faulty/m4_boat_hire_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}boat_hire_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 10 DECIMALS 2. + + TYPES: + BEGIN OF ty_result, + price TYPE ty_price, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + category TYPE ty_reason VALUE 'CATEGORY', + days TYPE ty_reason VALUE 'DAYS', + crew TYPE ty_reason VALUE 'CREW', + month TYPE ty_reason VALUE 'MONTH', + END OF c_reason. + + METHODS price + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_max_days TYPE i VALUE 28, + c_max_crew TYPE i VALUE 12, + c_max_month TYPE i VALUE 12, + c_free_crew TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 7, + c_long_hire_discount TYPE ty_price VALUE '0.90', + c_crew_surcharge TYPE ty_price VALUE '15.00'. + + METHODS validate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS calculate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_factor TYPE ty_price + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}boat_hire_pricer IMPLEMENTATION. + METHOD price. + DATA(lo_rates) = NEW {{p}}hire_rates( ). + DATA(lv_rate) = lo_rates->get_daily_rate( iv_category ). + + DATA(lv_reason) = validate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_month = iv_month ). + IF lv_reason <> c_reason-ok. + rs_result-reason = lv_reason. + RETURN. + ENDIF. + + rs_result-price = calculate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_factor = lo_rates->get_season_factor( iv_month ) ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD validate. + IF iv_rate <= 0. + rv_reason = c_reason-category. + ELSEIF iv_days < 1 OR iv_days > c_max_days. + rv_reason = c_reason-days. + ELSEIF iv_crew < 1 OR iv_crew > c_max_crew. + rv_reason = c_reason-crew. + ELSEIF iv_month < 1 OR iv_month > c_max_month. + rv_reason = c_reason-month. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD calculate. + DATA lv_price TYPE ty_price. + lv_price = iv_rate * iv_days * iv_factor. + + IF iv_days >= c_long_hire_days. + lv_price = lv_price * c_long_hire_discount. + ENDIF. + + IF iv_crew > c_free_crew. + lv_price = lv_price + ( iv_crew - c_free_crew ) / c_crew_surcharge * iv_days. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/generation.json b/tasks_gen/train/G1065/generation.json new file mode 100644 index 0000000..cf07a62 --- /dev/null +++ b/tasks_gen/train/G1065/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1065", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1065/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1065/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..08003ff --- /dev/null +++ b/tasks_gen/train/G1065/hidden/t02_hidden.clas.abap @@ -0,0 +1,196 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_208 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '208.00', + c_312 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '312.00', + c_468 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '468.00', + c_513 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '513.00', + c_936 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '936.00', + c_1344 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '1344.00', + c_6451 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '6451.20', + c_zero TYPE {{p}}boat_hire_pricer=>ty_price VALUE 0. + + DATA mo_cut TYPE REF TO {{p}}boat_hire_pricer. + + METHODS setup. + METHODS narrow_high_season FOR TESTING. + METHODS wide_long_hire FOR TESTING. + METHODS barge_low_season FOR TESTING. + METHODS lowercase_category FOR TESTING. + METHODS six_days_no_discount FOR TESTING. + METHODS crew_surcharge_boundary FOR TESTING. + METHODS max_bounds_ok FOR TESTING. + METHODS unknown_category FOR TESTING. + METHODS days_out_of_range FOR TESTING. + METHODS crew_out_of_range FOR TESTING. + METHODS month_out_of_range FOR TESTING. + METHODS reason_order FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}boat_hire_pricer( ). + ENDMETHOD. + + METHOD narrow_high_season. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_468 act = ls_result-price ). + ENDMETHOD. + + METHOD wide_long_hire. + DATA(ls_result) = mo_cut->price( iv_category = 'WIDE' + iv_days = 7 + iv_crew = 8 + iv_month = 5 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_1344 act = ls_result-price ). + ENDMETHOD. + + METHOD barge_low_season. + DATA(ls_result) = mo_cut->price( iv_category = 'BARGE' + iv_days = 1 + iv_crew = 1 + iv_month = 12 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_208 act = ls_result-price ). + ENDMETHOD. + + METHOD lowercase_category. + DATA(ls_result) = mo_cut->price( iv_category = 'narrow' + iv_days = 2 + iv_crew = 2 + iv_month = 6 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_312 act = ls_result-price ). + ENDMETHOD. + + METHOD six_days_no_discount. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 6 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = c_936 act = ls_result-price ). + ENDMETHOD. + + METHOD crew_surcharge_boundary. + DATA(ls_six) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 6 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = c_468 act = ls_six-price ). + + DATA(ls_seven) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 7 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = c_513 act = ls_seven-price ). + ENDMETHOD. + + METHOD max_bounds_ok. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 28 + iv_crew = 12 + iv_month = 6 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_6451 act = ls_result-price ). + ENDMETHOD. + + METHOD unknown_category. + DATA(ls_result) = mo_cut->price( iv_category = 'CANOE' + iv_days = 3 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-category + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_zero act = ls_result-price ). + ENDMETHOD. + + METHOD days_out_of_range. + DATA(ls_zero) = mo_cut->price( iv_category = 'NARROW' + iv_days = 0 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-days + act = ls_zero-reason ). + + DATA(ls_high) = mo_cut->price( iv_category = 'NARROW' + iv_days = 29 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-days + act = ls_high-reason ). + ENDMETHOD. + + METHOD crew_out_of_range. + DATA(ls_zero) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 0 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-crew + act = ls_zero-reason ). + + DATA(ls_high) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 13 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-crew + act = ls_high-reason ). + ENDMETHOD. + + METHOD month_out_of_range. + DATA(ls_zero) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 0 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-month + act = ls_zero-reason ). + + DATA(ls_high) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 13 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-month + act = ls_high-reason ). + ENDMETHOD. + + METHOD reason_order. + DATA(ls_result) = mo_cut->price( iv_category = 'CANOE' + iv_days = 0 + iv_crew = 0 + iv_month = 0 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-category + act = ls_result-reason msg = `category first` ). + + ls_result = mo_cut->price( iv_category = 'NARROW' + iv_days = 0 + iv_crew = 0 + iv_month = 0 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-days + act = ls_result-reason msg = `days before crew` ). + + ls_result = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 0 + iv_month = 0 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-crew + act = ls_result-reason msg = `crew before month` ). + + ls_result = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 0 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-month + act = ls_result-reason msg = `month last` ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/mutation.json b/tasks_gen/train/G1065/mutation.json new file mode 100644 index 0000000..aadcae4 --- /dev/null +++ b/tasks_gen/train/G1065/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1065", + "mutants": [ + { + "object": "{{P}}BOAT_HIRE_PRICER", + "mutant": "line 78: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "DAYS_OUT_OF_RANGE", + "REASON_ORDER" + ] + }, + { + "object": "{{P}}BOAT_HIRE_PRICER", + "mutant": "line 78: > -> <= (rel)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "BARGE_LOW_SEASON", + "CREW_OUT_OF_RANGE", + "CREW_SURCHARGE_BOUNDARY", + "DAYS_OUT_OF_RANGE", + "LOWERCASE_CATEGORY", + "MAX_BOUNDS_OK", + "MONTH_OUT_OF_RANGE", + "NARROW_HIGH_SEASON", + "REASON_ORDER", + "SIX_DAYS_NO_DISCOUNT", + "WIDE_LONG_HIRE" + ] + }, + { + "object": "{{P}}BOAT_HIRE_PRICER", + "mutant": "line 80: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "CREW_OUT_OF_RANGE", + "REASON_ORDER" + ] + }, + { + "object": "{{P}}BOAT_HIRE_PRICER", + "mutant": "line 82: 1 -> 2 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + }, + { + "object": "{{P}}BOAT_HIRE_PRICER", + "mutant": "line 98: * -> / (arith)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "CREW_SURCHARGE_BOUNDARY", + "MAX_BOUNDS_OK", + "WIDE_LONG_HIRE" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1065/reference/boat_hire_pricer.clas.abap b/tasks_gen/train/G1065/reference/boat_hire_pricer.clas.abap new file mode 100644 index 0000000..7362975 --- /dev/null +++ b/tasks_gen/train/G1065/reference/boat_hire_pricer.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}boat_hire_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_price TYPE p LENGTH 10 DECIMALS 2. + + TYPES: + BEGIN OF ty_result, + price TYPE ty_price, + reason TYPE ty_reason, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + category TYPE ty_reason VALUE 'CATEGORY', + days TYPE ty_reason VALUE 'DAYS', + crew TYPE ty_reason VALUE 'CREW', + month TYPE ty_reason VALUE 'MONTH', + END OF c_reason. + + METHODS price + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_max_days TYPE i VALUE 28, + c_max_crew TYPE i VALUE 12, + c_max_month TYPE i VALUE 12, + c_free_crew TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 7, + c_long_hire_discount TYPE ty_price VALUE '0.90', + c_crew_surcharge TYPE ty_price VALUE '15.00'. + + METHODS validate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS calculate + IMPORTING iv_rate TYPE ty_price + iv_days TYPE i + iv_crew TYPE i + iv_factor TYPE ty_price + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}boat_hire_pricer IMPLEMENTATION. + METHOD price. + DATA(lo_rates) = NEW {{p}}hire_rates( ). + DATA(lv_rate) = lo_rates->get_daily_rate( iv_category ). + + DATA(lv_reason) = validate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_month = iv_month ). + IF lv_reason <> c_reason-ok. + rs_result-reason = lv_reason. + RETURN. + ENDIF. + + rs_result-price = calculate( iv_rate = lv_rate + iv_days = iv_days + iv_crew = iv_crew + iv_factor = lo_rates->get_season_factor( iv_month ) ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD validate. + IF iv_rate <= 0. + rv_reason = c_reason-category. + ELSEIF iv_days < 1 OR iv_days > c_max_days. + rv_reason = c_reason-days. + ELSEIF iv_crew < 1 OR iv_crew > c_max_crew. + rv_reason = c_reason-crew. + ELSEIF iv_month < 1 OR iv_month > c_max_month. + rv_reason = c_reason-month. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD calculate. + DATA lv_price TYPE ty_price. + lv_price = iv_rate * iv_days * iv_factor. + + IF iv_days >= c_long_hire_days. + lv_price = lv_price * c_long_hire_discount. + ENDIF. + + IF iv_crew > c_free_crew. + lv_price = lv_price + ( iv_crew - c_free_crew ) * c_crew_surcharge * iv_days. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/reference/boat_hire_pricer.testclasses.abap b/tasks_gen/train/G1065/reference/boat_hire_pricer.testclasses.abap new file mode 100644 index 0000000..a28cc54 --- /dev/null +++ b/tasks_gen/train/G1065/reference/boat_hire_pricer.testclasses.abap @@ -0,0 +1,90 @@ +CLASS ltc_boat_hire_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_468 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '468.00', + c_1344 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '1344.00', + c_208 TYPE {{p}}boat_hire_pricer=>ty_price VALUE '208.00', + c_zero TYPE {{p}}boat_hire_pricer=>ty_price VALUE 0. + + DATA mo_cut TYPE REF TO {{p}}boat_hire_pricer. + + METHODS setup. + METHODS narrow_ok FOR TESTING. + METHODS wide_discount FOR TESTING. + METHODS low_season FOR TESTING. + METHODS unknown_category FOR TESTING. + METHODS bad_days FOR TESTING. + METHODS bad_crew FOR TESTING. + METHODS bad_month FOR TESTING. +ENDCLASS. + + +CLASS ltc_boat_hire_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}boat_hire_pricer( ). + ENDMETHOD. + + METHOD narrow_ok. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_468 act = ls_result-price ). + ENDMETHOD. + + METHOD wide_discount. + DATA(ls_result) = mo_cut->price( iv_category = 'WIDE' + iv_days = 7 + iv_crew = 8 + iv_month = 5 ). + cl_abap_unit_assert=>assert_equals( exp = c_1344 act = ls_result-price ). + ENDMETHOD. + + METHOD low_season. + DATA(ls_result) = mo_cut->price( iv_category = 'BARGE' + iv_days = 1 + iv_crew = 1 + iv_month = 12 ). + cl_abap_unit_assert=>assert_equals( exp = c_208 act = ls_result-price ). + ENDMETHOD. + + METHOD unknown_category. + DATA(ls_result) = mo_cut->price( iv_category = 'CANOE' + iv_days = 3 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-category + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_zero act = ls_result-price ). + ENDMETHOD. + + METHOD bad_days. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 0 + iv_crew = 4 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-days + act = ls_result-reason ). + ENDMETHOD. + + METHOD bad_crew. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 13 + iv_month = 7 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-crew + act = ls_result-reason ). + ENDMETHOD. + + METHOD bad_month. + DATA(ls_result) = mo_cut->price( iv_category = 'NARROW' + iv_days = 3 + iv_crew = 4 + iv_month = 13 ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}boat_hire_pricer=>c_reason-month + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/seed/hire_rates.clas.abap b/tasks_gen/train/G1065/seed/hire_rates.clas.abap new file mode 100644 index 0000000..60690ea --- /dev/null +++ b/tasks_gen/train/G1065/seed/hire_rates.clas.abap @@ -0,0 +1,40 @@ +CLASS {{p}}hire_rates DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rate TYPE p LENGTH 10 DECIMALS 2. + + METHODS get_daily_rate + IMPORTING iv_category TYPE string + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS get_season_factor + IMPORTING iv_month TYPE i + RETURNING VALUE(rv_factor) TYPE ty_rate. +ENDCLASS. + + +CLASS {{p}}hire_rates IMPLEMENTATION. + METHOD get_daily_rate. + CASE to_upper( iv_category ). + WHEN 'NARROW'. + rv_rate = 120. + WHEN 'WIDE'. + rv_rate = 180. + WHEN 'BARGE'. + rv_rate = 260. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_season_factor. + IF iv_month >= 6 AND iv_month <= 9. + rv_factor = '1.30'. + ELSEIF iv_month = 4 OR iv_month = 5 OR iv_month = 10. + rv_factor = '1.00'. + ELSEIF iv_month >= 1 AND iv_month <= 12. + rv_factor = '0.80'. + ELSE. + rv_factor = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1065/spec.md b/tasks_gen/train/G1065/spec.md new file mode 100644 index 0000000..58e1aea --- /dev/null +++ b/tasks_gen/train/G1065/spec.md @@ -0,0 +1,60 @@ +# 1. Goal +A canal boat hire company prices the hire of a narrowboat. The booking program +calls the calculation before it creates a booking confirmation. The calculation +returns the hire price in euro and a reason code. A valid request returns the +reason OK. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}HIRE_RATES exists in package $TMP. It is active and public. +- The class provides the daily rate of each boat category and the season factor + of each month. Read the class in the system to learn its interface, the + category names and the rates. Do not change the class. + +# 4. Contract +- Create the class {{P}}BOAT_HIRE_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Public types: + - TY_REASON, type c, length 10. + - TY_PRICE, type p, length 10, 2 decimals. + - TY_RESULT, a structure with the components PRICE (type TY_PRICE) and + REASON (type TY_REASON). +- Public constant structure C_REASON with the components OK, CATEGORY, DAYS, + CREW and MONTH. Each component has the type TY_REASON. The values are 'OK', + 'CATEGORY', 'DAYS', 'CREW' and 'MONTH'. +- One public method: + PRICE + IMPORTING iv_category TYPE string + iv_days TYPE i + iv_crew TYPE i + iv_month TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. The first rule that fails decides the reason. +If a rule fails, the result has this reason and the price 0. +1. The category must be a category that {{P}}HIRE_RATES knows. The category + name is not case-sensitive. If not, return CATEGORY. +2. iv_days must be between 1 and 28. If not, return DAYS. +3. iv_crew must be between 1 and 12. If not, return CREW. +4. iv_month must be between 1 and 12. If not, return MONTH. +5. Calculate the base price: the daily rate of the category multiplied by + iv_days multiplied by the season factor of iv_month. +6. If iv_days is 7 or more, subtract 10 % from the base price. +7. If iv_crew is more than 6, add 15.00 EUR for each crew member above 6 for + each day of the hire. +8. Round the result of rule 7 to 2 decimals. Return OK. + +# 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}}HIRE_RATES. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1065/task.json b/tasks_gen/train/G1065/task.json new file mode 100644 index 0000000..bb1dfdd --- /dev/null +++ b/tasks_gen/train/G1065/task.json @@ -0,0 +1,50 @@ +{ + "id": "G1065", + "category": "F", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}HIRE_RATES", + "file": "seed/hire_rates.clas.abap", + "description": "Daily rates per boat category and season factors per month" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}BOAT_HIRE_PRICER" + } + ], + "out_of_scope": [ + "{{P}}HIRE_RATES" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}BOAT_HIRE_PRICER", + "file": "reference/boat_hire_pricer.clas.abap", + "description": "Canal boat hire pricer", + "testclasses_file": "reference/boat_hire_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1066/faulty/m0_lib_fee.func.abap b/tasks_gen/train/G1066/faulty/m0_lib_fee.func.abap new file mode 100644 index 0000000..f472fb3 --- /dev/null +++ b/tasks_gen/train/G1066/faulty/m0_lib_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}lib_fee + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + DATA lv_fee TYPE decfloat34. + DATA lv_rate TYPE decfloat34. + + ev_blocked = abap_true. + IF iv_days_late < 0. + RAISE invalid_days. + ENDIF. + + CASE iv_media_type. + WHEN 'BOOK'. + lv_fee = iv_value * iv_days_late * 5 / 1000. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 5. + cv_fee = 5. + ENDIF. + WHEN 'DVD'. + lv_fee = iv_value * iv_days_late / 100. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 12. + cv_fee = 12. + ENDIF. + WHEN 'MAG'. + lv_rate = '0.30'. + cv_fee = iv_days_late * lv_rate. + WHEN 'REF'. + ev_blocked = abap_true. + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/faulty/m1_lib_fee.func.abap b/tasks_gen/train/G1066/faulty/m1_lib_fee.func.abap new file mode 100644 index 0000000..6a6457c --- /dev/null +++ b/tasks_gen/train/G1066/faulty/m1_lib_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}lib_fee + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + DATA lv_fee TYPE decfloat34. + DATA lv_rate TYPE decfloat34. + + ev_blocked = abap_false. + IF iv_days_late >= 0. + RAISE invalid_days. + ENDIF. + + CASE iv_media_type. + WHEN 'BOOK'. + lv_fee = iv_value * iv_days_late * 5 / 1000. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 5. + cv_fee = 5. + ENDIF. + WHEN 'DVD'. + lv_fee = iv_value * iv_days_late / 100. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 12. + cv_fee = 12. + ENDIF. + WHEN 'MAG'. + lv_rate = '0.30'. + cv_fee = iv_days_late * lv_rate. + WHEN 'REF'. + ev_blocked = abap_true. + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/faulty/m2_lib_fee.func.abap b/tasks_gen/train/G1066/faulty/m2_lib_fee.func.abap new file mode 100644 index 0000000..842acef --- /dev/null +++ b/tasks_gen/train/G1066/faulty/m2_lib_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}lib_fee + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + DATA lv_fee TYPE decfloat34. + DATA lv_rate TYPE decfloat34. + + ev_blocked = abap_false. + IF iv_days_late < 0. + RAISE invalid_days. + ENDIF. + + CASE iv_media_type. + WHEN 'BOOK'. + lv_fee = iv_value * iv_days_late / 5 / 1000. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 5. + cv_fee = 5. + ENDIF. + WHEN 'DVD'. + lv_fee = iv_value * iv_days_late / 100. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 12. + cv_fee = 12. + ENDIF. + WHEN 'MAG'. + lv_rate = '0.30'. + cv_fee = iv_days_late * lv_rate. + WHEN 'REF'. + ev_blocked = abap_true. + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/faulty/m3_lib_fee.func.abap b/tasks_gen/train/G1066/faulty/m3_lib_fee.func.abap new file mode 100644 index 0000000..78340e0 --- /dev/null +++ b/tasks_gen/train/G1066/faulty/m3_lib_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}lib_fee + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + DATA lv_fee TYPE decfloat34. + DATA lv_rate TYPE decfloat34. + + ev_blocked = abap_false. + IF iv_days_late < 0. + RAISE invalid_days. + ENDIF. + + CASE iv_media_type. + WHEN 'BOOK'. + lv_fee = iv_value * iv_days_late * 5 / 1000. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 5. + cv_fee = 5. + ENDIF. + WHEN 'DVD'. + lv_fee = iv_value / iv_days_late / 100. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 12. + cv_fee = 12. + ENDIF. + WHEN 'MAG'. + lv_rate = '0.30'. + cv_fee = iv_days_late * lv_rate. + WHEN 'REF'. + ev_blocked = abap_true. + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/generation.json b/tasks_gen/train/G1066/generation.json new file mode 100644 index 0000000..2cc1933 --- /dev/null +++ b/tasks_gen/train/G1066/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1066", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1066/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1066/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..05e7d60 --- /dev/null +++ b/tasks_gen/train/G1066/hidden/t41_hidden.clas.abap @@ -0,0 +1,135 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS get_fee + IMPORTING iv_type TYPE string + iv_days TYPE i + iv_value TYPE decfloat34 + iv_fee TYPE decfloat34 DEFAULT 0 + EXPORTING ev_blocked TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS book_percent FOR TESTING. + METHODS book_maximum FOR TESTING. + METHODS book_rounding FOR TESTING. + METHODS book_zero_days FOR TESTING. + METHODS book_not_blocked FOR TESTING. + METHODS dvd_percent FOR TESTING. + METHODS dvd_maximum FOR TESTING. + METHODS magazine_daily FOR TESTING. + METHODS reference_blocked FOR TESTING. + METHODS other_type_zero FOR TESTING. + METHODS negative_days FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + METHOD get_fee. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}LIB_FEE' + EXPORTING + iv_media_type = iv_type + iv_days_late = iv_days + iv_value = iv_value + IMPORTING + ev_blocked = ev_blocked + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD book_percent. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '1.50'. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = 3 iv_value = 100 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD book_maximum. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '5.00'. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = 2 iv_value = 1000 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD book_rounding. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '1.67'. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = 1 iv_value = 333 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD book_zero_days. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '0.00'. + DATA lv_rc TYPE sysubrc. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = 0 iv_value = 100 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD book_not_blocked. + DATA lv_blocked TYPE abap_bool. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = 1 iv_value = 100 + IMPORTING ev_blocked = lv_blocked ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_blocked ). + ENDMETHOD. + + METHOD dvd_percent. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '4.00'. + get_fee( EXPORTING iv_type = 'DVD' iv_days = 2 iv_value = 200 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD dvd_maximum. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '12.00'. + get_fee( EXPORTING iv_type = 'DVD' iv_days = 3 iv_value = 1000 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD magazine_daily. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '1.20'. + get_fee( EXPORTING iv_type = 'MAG' iv_days = 4 iv_value = 50 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD reference_blocked. + DATA lv_fee TYPE decfloat34. + DATA lv_blocked TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '7.00'. + get_fee( EXPORTING iv_type = 'REF' iv_days = 2 iv_value = 100 iv_fee = 7 + IMPORTING ev_blocked = lv_blocked ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_blocked ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD other_type_zero. + DATA lv_fee TYPE decfloat34. + DATA lv_blocked TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '0.00'. + get_fee( EXPORTING iv_type = 'XX' iv_days = 2 iv_value = 100 + IMPORTING ev_blocked = lv_blocked ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_blocked ). + ENDMETHOD. + + METHOD negative_days. + DATA lv_rc TYPE sysubrc. + get_fee( EXPORTING iv_type = 'BOOK' iv_days = -1 iv_value = 100 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1066/mutation.json b/tasks_gen/train/G1066/mutation.json new file mode 100644 index 0000000..c9eb1de --- /dev/null +++ b/tasks_gen/train/G1066/mutation.json @@ -0,0 +1,64 @@ +{ + "task": "G1066", + "mutants": [ + { + "object": "{{P}}LIB_FEE", + "mutant": "line 16: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BOOK_NOT_BLOCKED", + "OTHER_TYPE_ZERO" + ] + }, + { + "object": "{{P}}LIB_FEE", + "mutant": "line 17: < -> >= (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BOOK_MAXIMUM", + "BOOK_PERCENT", + "BOOK_ROUNDING", + "BOOK_ZERO_DAYS", + "DVD_MAXIMUM", + "DVD_PERCENT", + "MAGAZINE_DAILY", + "NEGATIVE_DAYS", + "REFERENCE_BLOCKED" + ] + }, + { + "object": "{{P}}LIB_FEE", + "mutant": "line 23: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "BOOK_MAXIMUM", + "BOOK_PERCENT", + "BOOK_ROUNDING" + ] + }, + { + "object": "{{P}}LIB_FEE", + "mutant": "line 29: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "DVD_MAXIMUM", + "DVD_PERCENT" + ] + }, + { + "object": "{{P}}LIB_FEE", + "mutant": "line 30: 2 -> 3 (const)", + "status": "survived", + "hidden": "11/11", + "failed_tests": [] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1066/reference/lib_fee.func.abap b/tasks_gen/train/G1066/reference/lib_fee.func.abap new file mode 100644 index 0000000..a9471b1 --- /dev/null +++ b/tasks_gen/train/G1066/reference/lib_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}lib_fee + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + DATA lv_fee TYPE decfloat34. + DATA lv_rate TYPE decfloat34. + + ev_blocked = abap_false. + IF iv_days_late < 0. + RAISE invalid_days. + ENDIF. + + CASE iv_media_type. + WHEN 'BOOK'. + lv_fee = iv_value * iv_days_late * 5 / 1000. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 5. + cv_fee = 5. + ENDIF. + WHEN 'DVD'. + lv_fee = iv_value * iv_days_late / 100. + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee > 12. + cv_fee = 12. + ENDIF. + WHEN 'MAG'. + lv_rate = '0.30'. + cv_fee = iv_days_late * lv_rate. + WHEN 'REF'. + ev_blocked = abap_true. + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/reference/t41_test.clas.abap b/tasks_gen/train/G1066/reference/t41_test.clas.abap new file mode 100644 index 0000000..41860e7 --- /dev/null +++ b/tasks_gen/train/G1066/reference/t41_test.clas.abap @@ -0,0 +1,81 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_type TYPE string + iv_days TYPE i + iv_value TYPE decfloat34 + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS book_fee FOR TESTING. + METHODS dvd_fee FOR TESTING. + METHODS fee_limits FOR TESTING. + METHODS magazine_fee FOR TESTING. + METHODS negative_days FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_blocked TYPE abap_bool. + CALL FUNCTION '{{P}}LIB_FEE' + EXPORTING + iv_media_type = iv_type + iv_days_late = iv_days + iv_value = iv_value + IMPORTING + ev_blocked = lv_blocked + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD book_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '1.50'. + lv_fee = fee_for( iv_type = 'BOOK' iv_days = 3 iv_value = 100 ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD dvd_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '4.00'. + lv_fee = fee_for( iv_type = 'DVD' iv_days = 2 iv_value = 200 ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD fee_limits. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '5.00'. + lv_fee = fee_for( iv_type = 'BOOK' iv_days = 2 iv_value = 1000 ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + lv_exp = '12.00'. + lv_fee = fee_for( iv_type = 'DVD' iv_days = 3 iv_value = 1000 ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD magazine_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '1.20'. + lv_fee = fee_for( iv_type = 'MAG' iv_days = 4 iv_value = 50 ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD negative_days. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}LIB_FEE' + EXPORTING + iv_media_type = 'BOOK' + iv_days_late = -1 + iv_value = 100 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1066/seed/lib_sample.func.abap b/tasks_gen/train/G1066/seed/lib_sample.func.abap new file mode 100644 index 0000000..556c6e9 --- /dev/null +++ b/tasks_gen/train/G1066/seed/lib_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}lib_sample + IMPORTING + iv_media_type TYPE string + iv_days_late TYPE i + iv_value TYPE decfloat34 + EXPORTING + ev_blocked TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + ev_blocked = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1066/spec.md b/tasks_gen/train/G1066/spec.md new file mode 100644 index 0000000..38f4593 --- /dev/null +++ b/tasks_gen/train/G1066/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A municipal library charges a fee when a borrower returns a media item after +the due date. The returns desk calls one function module for each returned +item. The function module must have the same interface as the sample function +module, because the returns desk calls all fee modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_LIB exists in package $TMP. +- Function module {{P}}LIB_SAMPLE exists in this group. It is the sample fee + module. Its interface is the interface that all fee modules must use. + +# 4. Contract +- Create the function module {{P}}LIB_FEE in function group {{P}}FG_LIB. +- Use exactly the interface of {{P}}LIB_SAMPLE: the same parameter names, + kinds, types, and exceptions. +- The parameters have this meaning: + - iv_media_type: the media type of the returned item. + - iv_days_late: the number of days between the due date and the return date. + - iv_value: the replacement value of the item in euro. + - ev_blocked: the borrower is blocked when this flag is true. + - cv_fee: the fee in euro. The caller can pass an initial value. + +# 5. Business rules +1. If iv_days_late is less than 0, raise the exception INVALID_DAYS. +2. Set ev_blocked to false, except in rule 6. +3. Media type BOOK: the fee is iv_value * iv_days_late * 0.5 / 100. Round the + fee half up to 2 decimals. The maximum fee is 5.00. +4. Media type DVD: the fee is iv_value * iv_days_late * 1 / 100. Round the fee + half up to 2 decimals. The maximum fee is 12.00. +5. Media type MAG: the fee is 0.30 for each day late. There is no maximum. +6. Media type REF: set ev_blocked to true. Do not change cv_fee. +7. Any other media type: the fee is 0. + +# 6. Constraints +- Release target: 7.02. Use only ABAP syntax that release 7.02 supports. +- Follow Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}LIB_SAMPLE. +- Do not use application tables. +- Write ABAP Unit tests for the function module in a global test class. + +# 7. Acceptance +- The function module {{P}}LIB_FEE is active and has no syntax error. +- The hidden tests pass. +- Your ABAP Unit tests pass. diff --git a/tasks_gen/train/G1066/task.json b/tasks_gen/train/G1066/task.json new file mode 100644 index 0000000..94367c7 --- /dev/null +++ b/tasks_gen/train/G1066/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1066", + "category": "G", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v702", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_LIB", + "description": "Function group for library fee modules" + }, + { + "type": "FUNC", + "name": "{{P}}LIB_SAMPLE", + "functionGroup": "{{P}}FG_LIB", + "file": "seed/lib_sample.func.abap", + "description": "Sample fee module that fixes the interface" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}LIB_FEE", + "functionGroup": "{{P}}FG_LIB", + "params": [ + { + "name": "iv_media_type", + "type": "string" + }, + { + "name": "iv_days_late", + "type": "i" + }, + { + "name": "iv_value", + "type": "decfloat34" + }, + { + "name": "ev_blocked", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}LIB_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}LIB_FEE", + "functionGroup": "{{P}}FG_LIB", + "file": "reference/lib_fee.func.abap", + "description": "Library overdue fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "T41 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1067/generation.json b/tasks_gen/train/G1067/generation.json new file mode 100644 index 0000000..a3224d7 --- /dev/null +++ b/tasks_gen/train/G1067/generation.json @@ -0,0 +1,31 @@ +{ + "id": "G1067", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/boattyp.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/boattyp.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "Too close to task G0150 (similarity spec 0.64, rules 0.61, names 0.50). Choose a different business topic and different object names." + ], + "by_check": { + "static": 2, + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1067/reference/tent_hire_fee.clas.abap b/tasks_gen/train/G1067/reference/tent_hire_fee.clas.abap new file mode 100644 index 0000000..2cd4f76 --- /dev/null +++ b/tasks_gen/train/G1067/reference/tent_hire_fee.clas.abap @@ -0,0 +1,69 @@ +CLASS {{p}}tent_hire_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_tent_hire. + PRIVATE SECTION. + TYPES ty_calc TYPE p LENGTH 15 DECIMALS 4. + + METHODS get_rates + IMPORTING iv_tent_size TYPE string + EXPORTING ev_rate_day TYPE {{p}}if_tent_hire=>ty_amount + ev_setup_fee TYPE {{p}}if_tent_hire=>ty_amount + ev_found TYPE abap_bool. + METHODS apply_discount + IMPORTING iv_days TYPE i + CHANGING cv_amount TYPE ty_calc. + METHODS round_gross + IMPORTING iv_amount TYPE ty_calc + RETURNING VALUE(rv_fee) TYPE {{p}}if_tent_hire=>ty_amount. +ENDCLASS. + + +CLASS {{p}}tent_hire_fee IMPLEMENTATION. + METHOD {{p}}if_tent_hire~calculate_fee. + DATA lv_rate_day TYPE {{p}}if_tent_hire=>ty_amount. + DATA lv_setup_fee TYPE {{p}}if_tent_hire=>ty_amount. + DATA lv_found TYPE abap_bool. + get_rates( + EXPORTING iv_tent_size = iv_tent_size + IMPORTING ev_rate_day = lv_rate_day + ev_setup_fee = lv_setup_fee + ev_found = lv_found ). + IF lv_found = abap_false. + rv_fee = 0. + RETURN. + ENDIF. + DATA lv_net TYPE ty_calc. + lv_net = iv_days * lv_rate_day + lv_setup_fee. + apply_discount( EXPORTING iv_days = iv_days CHANGING cv_amount = lv_net ). + DATA(lv_gross) = lv_net * ( 1 + {{p}}if_tent_hire=>c_vat ). + rv_fee = round_gross( lv_gross ). + ENDMETHOD. + + METHOD get_rates. + CASE to_upper( iv_tent_size ). + WHEN 'S'. + ev_rate_day = {{p}}if_tent_hire=>c_rate_s-rate_day. + ev_setup_fee = {{p}}if_tent_hire=>c_rate_s-setup_fee. + WHEN 'M'. + ev_rate_day = {{p}}if_tent_hire=>c_rate_m-rate_day. + ev_setup_fee = {{p}}if_tent_hire=>c_rate_m-setup_fee. + WHEN 'L'. + ev_rate_day = {{p}}if_tent_hire=>c_rate_l-rate_day. + ev_setup_fee = {{p}}if_tent_hire=>c_rate_l-setup_fee. + WHEN OTHERS. + ev_found = abap_false. + RETURN. + ENDCASE. + ev_found = abap_true. + ENDMETHOD. + + METHOD apply_discount. + IF iv_days >= {{p}}if_tent_hire=>c_long_rental_days. + cv_amount = cv_amount * ( 1 - {{p}}if_tent_hire=>c_discount ). + ENDIF. + ENDMETHOD. + + METHOD round_gross. + rv_fee = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1067/reference/tent_hire_fee.testclasses.abap b/tasks_gen/train/G1067/reference/tent_hire_fee.testclasses.abap new file mode 100644 index 0000000..50fdca0 --- /dev/null +++ b/tasks_gen/train/G1067/reference/tent_hire_fee.testclasses.abap @@ -0,0 +1,51 @@ +CLASS ltc_tent_hire_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_tent_hire. + METHODS setup. + METHODS small_tent_one_day FOR TESTING. + METHODS small_tent_five_days FOR TESTING. + METHODS medium_tent_three_days FOR TESTING. + METHODS large_tent_seven_days FOR TESTING. + METHODS unknown_size FOR TESTING. +ENDCLASS. + + +CLASS ltc_tent_hire_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tent_hire_fee( ). + ENDMETHOD. + + METHOD small_tent_one_day. + " 1 * 45.00 + 30.00 = 75.00; plus 19% VAT = 89.25 + cl_abap_unit_assert=>assert_equals( + exp = '89.25' + act = mo_cut->calculate_fee( iv_tent_size = 'S' iv_days = 1 ) ). + ENDMETHOD. + + METHOD small_tent_five_days. + " 5 * 45.00 + 30.00 = 255.00; less 10% = 229.50; plus 19% VAT = 273.105 + cl_abap_unit_assert=>assert_equals( + exp = '273.11' + act = mo_cut->calculate_fee( iv_tent_size = 'S' iv_days = 5 ) ). + ENDMETHOD. + + METHOD medium_tent_three_days. + " 3 * 68.00 + 40.00 = 244.00; plus 19% VAT = 290.36 + cl_abap_unit_assert=>assert_equals( + exp = '290.36' + act = mo_cut->calculate_fee( iv_tent_size = 'M' iv_days = 3 ) ). + ENDMETHOD. + + METHOD large_tent_seven_days. + " 7 * 95.00 + 55.00 = 720.00; less 10% = 648.00; plus 19% VAT = 771.12 + cl_abap_unit_assert=>assert_equals( + exp = '771.12' + act = mo_cut->calculate_fee( iv_tent_size = 'L' iv_days = 7 ) ). + ENDMETHOD. + + METHOD unknown_size. + cl_abap_unit_assert=>assert_equals( + exp = '0.00' + act = mo_cut->calculate_fee( iv_tent_size = 'X' iv_days = 2 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1067/seed/if_tent_hire.intf.abap b/tasks_gen/train/G1067/seed/if_tent_hire.intf.abap new file mode 100644 index 0000000..ad9f1ac --- /dev/null +++ b/tasks_gen/train/G1067/seed/if_tent_hire.intf.abap @@ -0,0 +1,25 @@ +INTERFACE {{p}}if_tent_hire PUBLIC. + TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_rate_s, + rate_day TYPE ty_amount VALUE '45.00', + setup_fee TYPE ty_amount VALUE '30.00', + END OF c_rate_s, + BEGIN OF c_rate_m, + rate_day TYPE ty_amount VALUE '68.00', + setup_fee TYPE ty_amount VALUE '40.00', + END OF c_rate_m, + BEGIN OF c_rate_l, + rate_day TYPE ty_amount VALUE '95.00', + setup_fee TYPE ty_amount VALUE '55.00', + END OF c_rate_l, + c_vat TYPE ty_amount VALUE '0.19', + c_discount TYPE ty_amount VALUE '0.10', + c_long_rental_days TYPE i VALUE 5. + + METHODS calculate_fee + IMPORTING iv_tent_size TYPE string + iv_days TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDINTERFACE. diff --git a/tasks_gen/train/G1067/spec.md b/tasks_gen/train/G1067/spec.md new file mode 100644 index 0000000..3c43c93 --- /dev/null +++ b/tasks_gen/train/G1067/spec.md @@ -0,0 +1,49 @@ +# 1. Goal +Calculate the fee of hiring a marquee tent for an event. A booking program +calls the calculation before it confirms a booking. The program needs the +gross price in euro. + +# 2. Open questions +None. + +# 3. Context +- Interface {{P}}IF_TENT_HIRE exists in package $TMP. It is active. +- The interface has the type TY_AMOUNT (P LENGTH 15 DECIMALS 2), the constant + structures C_RATE_S, C_RATE_M and C_RATE_L, the constants C_VAT, + C_DISCOUNT and C_LONG_RENTAL_DAYS, and one method: + CALCULATE_FEE IMPORTING iv_tent_size TYPE string + iv_days TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +- Each rate structure has the components RATE_DAY and SETUP_FEE. + C_RATE_S belongs to tent size S, C_RATE_M to tent size M and C_RATE_L to + tent size L. +- C_VAT is the value added tax rate. C_DISCOUNT is the discount rate. + C_LONG_RENTAL_DAYS is the number of days from which the discount applies. + +# 4. Contract +- Create the class {{P}}TENT_HIRE_FEE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_TENT_HIRE. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. Read the daily rate and the setup fee for the tent size. Tent size S uses + C_RATE_S, tent size M uses C_RATE_M and tent size L uses C_RATE_L. If the + tent size is not S, M or L, return 0. +2. The net price is the number of days multiplied by the daily rate plus the + setup fee. +3. If the number of days is C_LONG_RENTAL_DAYS or more, reduce the net price by + C_DISCOUNT. +4. Add value added tax of C_VAT to the net price. The result is the gross price + in euro. The gross price must have two decimal places. + +# 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_TENT_HIRE. + +# 7. Acceptance +- The class is active and has no syntax error. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1067/task.json b/tasks_gen/train/G1067/task.json new file mode 100644 index 0000000..cc132d9 --- /dev/null +++ b/tasks_gen/train/G1067/task.json @@ -0,0 +1,64 @@ +{ + "id": "G1067", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The spec does not define the rounding rule that turns the gross price into an amount with two decimal places.", + "keywords": [ + [ + "rounding", + "rule" + ], + [ + "round", + "cent" + ], + [ + "round", + "decimal" + ], + [ + "how", + "round" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_TENT_HIRE", + "file": "seed/if_tent_hire.intf.abap", + "description": "Marquee tent hire interface with rates" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TENT_HIRE_FEE", + "implements": "{{P}}IF_TENT_HIRE" + } + ], + "out_of_scope": [ + "{{P}}IF_TENT_HIRE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TENT_HIRE_FEE", + "file": "reference/tent_hire_fee.clas.abap", + "description": "Marquee tent hire fee calculation", + "testclasses_file": "reference/tent_hire_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1068/generation.json b/tasks_gen/train/G1068/generation.json new file mode 100644 index 0000000..49043f1 --- /dev/null +++ b/tasks_gen/train/G1068/generation.json @@ -0,0 +1,44 @@ +{ + "id": "G1068", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t16_test.clas.abap line 40: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 80: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 86: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 92: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 5 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "reference/t16_test.clas.abap line 44: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 81: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 86: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1068/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1068/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..b075e79 --- /dev/null +++ b/tasks_gen/train/G1068/hidden/t16_hidden.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}t16_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS stay_rows FOR TESTING. + METHODS hotel_rows FOR TESTING. + METHODS guest_shown FOR TESTING. + METHODS total_price FOR TESTING. + METHODS price_decimals FOR TESTING. + METHODS town_shown FOR TESTING. + METHODS rating_shown FOR TESTING. + METHODS unknown_hotel FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_STAY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_hotel TYPE STANDARD TABLE OF {{p}}hotel WITH EMPTY KEY. + DATA lt_stay TYPE STANDARD TABLE OF {{p}}stay WITH EMPTY KEY. + + go_env->clear_doubles( ). + + lt_hotel = VALUE #( ( hotel_id = 'H1' city_name = 'HAMBURG' star_rating = 4 ) + ( hotel_id = 'H2' city_name = 'MUNICH' star_rating = 5 ) ). + + lt_stay = VALUE #( ( stay_id = 'S1' hotel_id = 'H1' guest = 'ADA' nights = 3 price_per_night = '100.00' ) + ( stay_id = 'S2' hotel_id = 'H1' guest = 'BOB' nights = 1 price_per_night = '99.99' ) + ( stay_id = 'S3' hotel_id = 'H2' guest = 'CARA' nights = 2 price_per_night = '10.50' ) + ( stay_id = 'S4' hotel_id = 'H9' guest = 'DAN' nights = 5 price_per_night = '1.00' ) ). + + go_env->insert_test_data( lt_hotel ). + go_env->insert_test_data( lt_stay ). + ENDMETHOD. + + METHOD stay_rows. + SELECT COUNT(*) FROM {{p}}i_stay INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rows ). + ENDMETHOD. + + METHOD hotel_rows. + SELECT COUNT(*) FROM {{p}}i_hotel INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD guest_shown. + SELECT SINGLE guest FROM {{p}}i_stay WHERE stay_id = 'S2' INTO @DATA(lv_guest). + cl_abap_unit_assert=>assert_equals( exp = 'BOB' act = lv_guest ). + ENDMETHOD. + + METHOD total_price. + SELECT SINGLE total_price FROM {{p}}i_stay WHERE stay_id = 'S1' INTO @DATA(lv_price). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '300.00' ) act = CONV decfloat34( lv_price ) ). + ENDMETHOD. + + METHOD price_decimals. + SELECT SINGLE total_price FROM {{p}}i_stay WHERE stay_id = 'S3' INTO @DATA(lv_price). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '21.00' ) act = CONV decfloat34( lv_price ) ). + ENDMETHOD. + + METHOD town_shown. + SELECT SINGLE city_name FROM {{p}}i_stay WHERE stay_id = 'S1' INTO @DATA(lv_town). + cl_abap_unit_assert=>assert_equals( exp = 'HAMBURG' act = lv_town ). + ENDMETHOD. + + METHOD rating_shown. + SELECT SINGLE star_rating FROM {{p}}i_stay WHERE stay_id = 'S3' INTO @DATA(lv_rating). + cl_abap_unit_assert=>assert_equals( exp = CONV i( 5 ) act = CONV i( lv_rating ) ). + ENDMETHOD. + + METHOD unknown_hotel. + SELECT SINGLE city_name, star_rating FROM {{p}}i_stay WHERE stay_id = 'S4' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_initial( ls_row-city_name ). + cl_abap_unit_assert=>assert_initial( ls_row-star_rating ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1068/reference/i_hotel.ddls.asddls b/tasks_gen/train/G1068/reference/i_hotel.ddls.asddls new file mode 100644 index 0000000..583c17f --- /dev/null +++ b/tasks_gen/train/G1068/reference/i_hotel.ddls.asddls @@ -0,0 +1,9 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Hotel' +define view entity {{P}}I_HOTEL + as select from {{p}}hotel +{ + key hotel_id as hotel_id, + city_name as city_name, + star_rating as star_rating +} diff --git a/tasks_gen/train/G1068/reference/i_stay.ddls.asddls b/tasks_gen/train/G1068/reference/i_stay.ddls.asddls new file mode 100644 index 0000000..f399549 --- /dev/null +++ b/tasks_gen/train/G1068/reference/i_stay.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Hotel stay' +define view entity {{P}}I_STAY + as select from {{p}}stay + left outer join {{p}}hotel as hotel on hotel.hotel_id = stay.hotel_id + association [0..1] to {{P}}I_HOTEL as _Hotel + on $projection.hotel_id = _Hotel.hotel_id +{ + key stay.stay_id as stay_id, + stay.hotel_id as hotel_id, + stay.guest as guest, + stay.nights as nights, + cast( stay.nights as abap.dec(15,2) ) * stay.price_per_night as total_price, + hotel.city_name as city_name, + hotel.star_rating as star_rating +} diff --git a/tasks_gen/train/G1068/reference/t16_test.clas.abap b/tasks_gen/train/G1068/reference/t16_test.clas.abap new file mode 100644 index 0000000..2149e70 --- /dev/null +++ b/tasks_gen/train/G1068/reference/t16_test.clas.abap @@ -0,0 +1,42 @@ +CLASS {{p}}t16_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS price_and_town FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_STAY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_hotel TYPE STANDARD TABLE OF {{p}}hotel WITH EMPTY KEY. + DATA lt_stay TYPE STANDARD TABLE OF {{p}}stay WITH EMPTY KEY. + + go_env->clear_doubles( ). + + lt_hotel = VALUE #( ( hotel_id = 'X1' city_name = 'BONN' star_rating = 3 ) ). + lt_stay = VALUE #( ( stay_id = 'T1' hotel_id = 'X1' guest = 'EVE' nights = 4 price_per_night = '25.00' ) ). + + go_env->insert_test_data( lt_hotel ). + go_env->insert_test_data( lt_stay ). + ENDMETHOD. + + METHOD price_and_town. + SELECT SINGLE total_price, city_name FROM {{p}}i_stay WHERE stay_id = 'T1' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '100.00' ) act = CONV decfloat34( ls_row-total_price ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BONN' act = ls_row-city_name ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1068/seed/hotel.tabl.asabap b/tasks_gen/train/G1068/seed/hotel.tabl.asabap new file mode 100644 index 0000000..a4d0de2 --- /dev/null +++ b/tasks_gen/train/G1068/seed/hotel.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Hotel' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}hotel { + key client : abap.clnt not null; + key hotel_id : abap.char(10) not null; + city_name : abap.char(20); + star_rating : abap.int1; +} diff --git a/tasks_gen/train/G1068/seed/seed_data.clas.abap b/tasks_gen/train/G1068/seed/seed_data.clas.abap new file mode 100644 index 0000000..dc3ffc0 --- /dev/null +++ b/tasks_gen/train/G1068/seed/seed_data.clas.abap @@ -0,0 +1,37 @@ +CLASS {{p}}seed_data DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PRIVATE SECTION. + METHODS fill_tables. +ENDCLASS. + + +CLASS {{p}}seed_data IMPLEMENTATION. + + METHOD if_oo_adt_classrun~main. + fill_tables( ). + out->write( 'Hotel and stay data written.' ). + ENDMETHOD. + + METHOD fill_tables. + DATA lt_hotel TYPE STANDARD TABLE OF {{p}}hotel WITH EMPTY KEY. + DATA lt_stay TYPE STANDARD TABLE OF {{p}}stay WITH EMPTY KEY. + + lt_hotel = VALUE #( + ( hotel_id = 'H1' city_name = 'HAMBURG' star_rating = 4 ) + ( hotel_id = 'H2' city_name = 'MUNICH' star_rating = 5 ) + ( hotel_id = 'H3' city_name = 'COLOGNE' star_rating = 3 ) ). + + lt_stay = VALUE #( + ( stay_id = 'S1' hotel_id = 'H1' guest = 'ADA' nights = 3 price_per_night = '100.00' ) + ( stay_id = 'S2' hotel_id = 'H1' guest = 'BOB' nights = 1 price_per_night = '99.99' ) + ( stay_id = 'S3' hotel_id = 'H2' guest = 'CARA' nights = 2 price_per_night = '10.50' ) + ( stay_id = 'S4' hotel_id = 'H3' guest = 'DAN' nights = 5 price_per_night = '1.00' ) ). + + DELETE FROM {{p}}stay. + DELETE FROM {{p}}hotel. + INSERT {{p}}hotel FROM TABLE @lt_hotel. + INSERT {{p}}stay FROM TABLE @lt_stay. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1068/seed/stay.tabl.asabap b/tasks_gen/train/G1068/seed/stay.tabl.asabap new file mode 100644 index 0000000..043920a --- /dev/null +++ b/tasks_gen/train/G1068/seed/stay.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Hotel stay' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}stay { + key client : abap.clnt not null; + key stay_id : abap.char(10) not null; + hotel_id : abap.char(10); + guest : abap.char(20); + nights : abap.int4; + price_per_night : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1068/spec.md b/tasks_gen/train/G1068/spec.md new file mode 100644 index 0000000..c5d419b --- /dev/null +++ b/tasks_gen/train/G1068/spec.md @@ -0,0 +1,46 @@ +# 1. Goal +Show each hotel stay together with the data of its hotel. A report and an OData +service read the stays. The stay view exposes the association _HOTEL, so that +the consumers can read the hotel data of a stay through the association. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}HOTEL (hotel), package $TMP. Key: HOTEL_ID (CHAR 10). + Fields: CITY_NAME (CHAR 20), STAR_RATING (INT1). +- Table {{P}}STAY (hotel stay), package $TMP. Key: STAY_ID (CHAR 10). + Fields: HOTEL_ID (CHAR 10), GUEST (CHAR 20), NIGHTS (INT4), + PRICE_PER_NIGHT (DEC 15,2, price for one night). + +# 4. Contract +- Create the CDS view entity {{P}}I_HOTEL in package $TMP. + Elements, with these names: HOTEL_ID (key), CITY_NAME, STAR_RATING. +- Create the CDS view entity {{P}}I_STAY in package $TMP. + Elements, with these names: STAY_ID (key), HOTEL_ID, GUEST, NIGHTS, + TOTAL_PRICE, CITY_NAME, STAR_RATING. +- {{P}}I_STAY has the association _HOTEL. _HOTEL leads to {{P}}I_HOTEL and is + part of the interface of {{P}}I_STAY. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. {{P}}I_HOTEL shows one row for each row of {{P}}HOTEL. +2. {{P}}I_STAY shows one row for each row of {{P}}STAY. +3. CITY_NAME and STAR_RATING of a stay are the CITY_NAME and the STAR_RATING + of the hotel of the stay. +4. TOTAL_PRICE is NIGHTS multiplied by PRICE_PER_NIGHT. TOTAL_PRICE has 2 + decimals. +5. A stay belongs to zero or one hotel. A stay can point to a hotel that is not + in {{P}}HOTEL. In this case {{P}}I_STAY still shows the stay, and CITY_NAME + and STAR_RATING are empty. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Methods have fewer than 40 statements. Pass + large parameters by reference. +- Out of scope: do not change {{P}}HOTEL and {{P}}STAY. + +# 7. Acceptance +- The views are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1068/task.json b/tasks_gen/train/G1068/task.json new file mode 100644 index 0000000..f5e54aa --- /dev/null +++ b/tasks_gen/train/G1068/task.json @@ -0,0 +1,90 @@ +{ + "id": "G1068", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}HOTEL", + "file": "seed/hotel.tabl.asabap", + "description": "Hotel master data" + }, + { + "type": "TABL", + "name": "{{P}}STAY", + "file": "seed/stay.tabl.asabap", + "description": "Hotel stays" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DATA", + "file": "seed/seed_data.clas.abap", + "description": "Seed data for hotel and stay", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_HOTEL", + "fields": [ + "HOTEL_ID", + "CITY_NAME", + "STAR_RATING" + ] + }, + { + "type": "DDLS", + "name": "{{P}}I_STAY", + "fields": [ + "STAY_ID", + "HOTEL_ID", + "GUEST", + "NIGHTS", + "TOTAL_PRICE", + "CITY_NAME", + "STAR_RATING" + ] + } + ], + "out_of_scope": [ + "{{P}}HOTEL", + "{{P}}STAY" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_HOTEL", + "file": "reference/i_hotel.ddls.asddls", + "description": "Hotel" + }, + { + "type": "DDLS", + "name": "{{P}}I_STAY", + "file": "reference/i_stay.ddls.asddls", + "description": "Stay with the association to the hotel" + }, + { + "type": "CLAS", + "name": "{{P}}T16_TEST", + "file": "reference/t16_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1069/faulty/m0_league_table.clas.abap b/tasks_gen/train/G1069/faulty/m0_league_table.clas.abap new file mode 100644 index 0000000..dfbd8f3 --- /dev/null +++ b/tasks_gen/train/G1069/faulty/m0_league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_true. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + OR is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played + 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for = iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for <> ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/faulty/m1_league_table.clas.abap b/tasks_gen/train/G1069/faulty/m1_league_table.clas.abap new file mode 100644 index 0000000..dc5a949 --- /dev/null +++ b/tasks_gen/train/G1069/faulty/m1_league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_false. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + AND is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played + 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for = iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for <> ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/faulty/m2_league_table.clas.abap b/tasks_gen/train/G1069/faulty/m2_league_table.clas.abap new file mode 100644 index 0000000..f7d0177 --- /dev/null +++ b/tasks_gen/train/G1069/faulty/m2_league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_false. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + OR is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played - 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for = iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for <> ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/faulty/m3_league_table.clas.abap b/tasks_gen/train/G1069/faulty/m3_league_table.clas.abap new file mode 100644 index 0000000..f51be25 --- /dev/null +++ b/tasks_gen/train/G1069/faulty/m3_league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_false. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + OR is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played + 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for <> iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for <> ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/faulty/m4_league_table.clas.abap b/tasks_gen/train/G1069/faulty/m4_league_table.clas.abap new file mode 100644 index 0000000..eae10a8 --- /dev/null +++ b/tasks_gen/train/G1069/faulty/m4_league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_false. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + OR is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played + 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for = iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for = ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/generation.json b/tasks_gen/train/G1069/generation.json new file mode 100644 index 0000000..a804bbe --- /dev/null +++ b/tasks_gen/train/G1069/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1069", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1069/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1069/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..84cd8d5 --- /dev/null +++ b/tasks_gen/train/G1069/hidden/t31_hidden.clas.abap @@ -0,0 +1,142 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}league_table. + + METHODS setup. + METHODS basic_league + RETURNING VALUE(rt_matches) TYPE {{p}}league_table=>tt_match. + + METHODS empty_input_is_empty FOR TESTING. + METHODS ignores_invalid_matches FOR TESTING. + METHODS counts_played_and_points FOR TESTING. + METHODS accumulates_goals FOR TESTING. + METHODS sorts_by_points FOR TESTING. + METHODS sorts_by_goal_diff FOR TESTING. + METHODS sorts_by_goals_for FOR TESTING. + METHODS sorts_by_team_name FOR TESTING. + METHODS ties_share_rank FOR TESTING. + METHODS rank_jumps_after_tie FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}league_table( ). + ENDMETHOD. + + METHOD basic_league. + rt_matches = VALUE #( + ( home_team = 'A' away_team = 'B' home_goals = 2 away_goals = 0 ) + ( home_team = 'A' away_team = 'C' home_goals = 1 away_goals = 1 ) + ( home_team = 'B' away_team = 'C' home_goals = 3 away_goals = 1 ) + ( home_team = 'C' away_team = 'D' home_goals = 2 away_goals = 2 ) + ( home_team = 'D' away_team = 'A' home_goals = 0 away_goals = 3 ) ). + ENDMETHOD. + + METHOD empty_input_is_empty. + DATA(lt_table) = mo_cut->evaluate( VALUE {{p}}league_table=>tt_match( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_table ) ). + ENDMETHOD. + + METHOD ignores_invalid_matches. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = '' away_team = 'B' home_goals = 1 away_goals = 0 ) + ( home_team = 'A' away_team = '' home_goals = 1 away_goals = 0 ) + ( home_team = 'A' away_team = 'A' home_goals = 1 away_goals = 0 ) + ( home_team = 'A' away_team = 'B' home_goals = -1 away_goals = 0 ) + ( home_team = 'A' away_team = 'B' home_goals = 0 away_goals = -2 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_table ) ). + ENDMETHOD. + + METHOD counts_played_and_points. + DATA(lt_table) = mo_cut->evaluate( basic_league( ) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_table ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_table[ 1 ]-played ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_table[ 1 ]-won ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-drawn ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_table[ 1 ]-lost ). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lt_table[ 1 ]-points ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-rank ). + ENDMETHOD. + + METHOD accumulates_goals. + DATA(lt_table) = mo_cut->evaluate( basic_league( ) ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lt_table[ 1 ]-goals_for ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-goals_against ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lt_table[ 1 ]-goal_diff ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_table[ 4 ]-goals_for ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lt_table[ 4 ]-goals_against ). + cl_abap_unit_assert=>assert_equals( exp = -3 act = lt_table[ 4 ]-goal_diff ). + ENDMETHOD. + + METHOD sorts_by_points. + DATA(lt_table) = mo_cut->evaluate( basic_league( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_table[ 2 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'C' act = lt_table[ 3 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'D' act = lt_table[ 4 ]-team ). + ENDMETHOD. + + METHOD sorts_by_goal_diff. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = 'P' away_team = 'R' home_goals = 3 away_goals = 0 ) + ( home_team = 'Q' away_team = 'S' home_goals = 1 away_goals = 0 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 'P' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'Q' act = lt_table[ 2 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_table[ 3 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'R' act = lt_table[ 4 ]-team ). + ENDMETHOD. + + METHOD sorts_by_goals_for. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = 'P' away_team = 'R' home_goals = 2 away_goals = 0 ) + ( home_team = 'Q' away_team = 'S' home_goals = 3 away_goals = 1 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 'Q' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'P' act = lt_table[ 2 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_table[ 3 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'R' act = lt_table[ 4 ]-team ). + ENDMETHOD. + + METHOD sorts_by_team_name. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = 'ZEBRA' away_team = 'Y' home_goals = 1 away_goals = 0 ) + ( home_team = 'ALPHA' away_team = 'X' home_goals = 1 away_goals = 0 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'ZEBRA' act = lt_table[ 2 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'X' act = lt_table[ 3 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 'Y' act = lt_table[ 4 ]-team ). + ENDMETHOD. + + METHOD ties_share_rank. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = 'E' away_team = 'F' home_goals = 1 away_goals = 1 ) + ( home_team = 'G' away_team = 'H' home_goals = 1 away_goals = 1 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_table ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 2 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 3 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 4 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-points ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-drawn ). + ENDMETHOD. + + METHOD rank_jumps_after_tie. + DATA(lt_matches) = VALUE {{p}}league_table=>tt_match( + ( home_team = 'A' away_team = 'B' home_goals = 1 away_goals = 0 ) + ( home_team = 'C' away_team = 'D' home_goals = 1 away_goals = 1 ) ). + DATA(lt_table) = mo_cut->evaluate( lt_matches ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_table[ 2 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_table[ 3 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_table[ 4 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_table[ 4 ]-team ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/mutation.json b/tasks_gen/train/G1069/mutation.json new file mode 100644 index 0000000..169a7fd --- /dev/null +++ b/tasks_gen/train/G1069/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1069", + "mutants": [ + { + "object": "{{P}}LEAGUE_TABLE", + "mutant": "line 56: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "ACCUMULATES_GOALS", + "COUNTS_PLAYED_AND_POINTS", + "IGNORES_INVALID_MATCHES", + "RANK_JUMPS_AFTER_TIE", + "SORTS_BY_GOALS_FOR", + "SORTS_BY_GOAL_DIFF", + "SORTS_BY_POINTS", + "SORTS_BY_TEAM_NAME", + "TIES_SHARE_RANK" + ] + }, + { + "object": "{{P}}LEAGUE_TABLE", + "mutant": "line 79: OR -> AND (logic)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "IGNORES_INVALID_MATCHES" + ] + }, + { + "object": "{{P}}LEAGUE_TABLE", + "mutant": "line 90: + -> - (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "COUNTS_PLAYED_AND_POINTS" + ] + }, + { + "object": "{{P}}LEAGUE_TABLE", + "mutant": "line 98: = -> <> (eq)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "COUNTS_PLAYED_AND_POINTS", + "RANK_JUMPS_AFTER_TIE", + "TIES_SHARE_RANK" + ] + }, + { + "object": "{{P}}LEAGUE_TABLE", + "mutant": "line 116: <> -> = (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "RANK_JUMPS_AFTER_TIE", + "TIES_SHARE_RANK" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1069/reference/league_table.clas.abap b/tasks_gen/train/G1069/reference/league_table.clas.abap new file mode 100644 index 0000000..801b626 --- /dev/null +++ b/tasks_gen/train/G1069/reference/league_table.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}league_table DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_team TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_match, + home_team TYPE ty_team, + away_team TYPE ty_team, + home_goals TYPE i, + away_goals TYPE i, + END OF ty_match. + TYPES tt_match TYPE STANDARD TABLE OF ty_match WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_row, + rank TYPE i, + team TYPE ty_team, + played TYPE i, + won TYPE i, + drawn TYPE i, + lost TYPE i, + goals_for TYPE i, + goals_against TYPE i, + goal_diff TYPE i, + points TYPE i, + END OF ty_row. + TYPES tt_row TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + METHODS evaluate + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. + + PRIVATE SECTION. + TYPES tt_work TYPE HASHED TABLE OF ty_row WITH UNIQUE KEY team. + + METHODS is_invalid + IMPORTING is_match TYPE ty_match + RETURNING VALUE(rv_bad) TYPE abap_bool. + + METHODS add_result + IMPORTING iv_team TYPE ty_team + iv_goals_for TYPE i + iv_goals_against TYPE i + CHANGING ct_work TYPE tt_work. + + METHODS assign_rank + CHANGING ct_table TYPE tt_row. +ENDCLASS. + + +CLASS {{p}}league_table IMPLEMENTATION. + METHOD evaluate. + DATA lt_work TYPE tt_work. + + LOOP AT it_matches INTO DATA(ls_match). + IF is_invalid( ls_match ) = abap_false. + add_result( EXPORTING iv_team = ls_match-home_team + iv_goals_for = ls_match-home_goals + iv_goals_against = ls_match-away_goals + CHANGING ct_work = lt_work ). + add_result( EXPORTING iv_team = ls_match-away_team + iv_goals_for = ls_match-away_goals + iv_goals_against = ls_match-home_goals + CHANGING ct_work = lt_work ). + ENDIF. + ENDLOOP. + + rt_table = lt_work. + SORT rt_table BY points DESCENDING + goal_diff DESCENDING + goals_for DESCENDING + team ASCENDING. + assign_rank( CHANGING ct_table = rt_table ). + ENDMETHOD. + + METHOD is_invalid. + rv_bad = xsdbool( is_match-home_team IS INITIAL + OR is_match-away_team IS INITIAL + OR is_match-home_team = is_match-away_team + OR is_match-home_goals < 0 + OR is_match-away_goals < 0 ). + ENDMETHOD. + + METHOD add_result. + ASSIGN ct_work[ team = iv_team ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_row( team = iv_team ) INTO TABLE ct_work ASSIGNING . + ENDIF. + + -played = -played + 1. + -goals_for = -goals_for + iv_goals_for. + -goals_against = -goals_against + iv_goals_against. + -goal_diff = -goals_for - -goals_against. + + IF iv_goals_for > iv_goals_against. + -won = -won + 1. + -points = -points + 3. + ELSEIF iv_goals_for = iv_goals_against. + -drawn = -drawn + 1. + -points = -points + 1. + ELSE. + -lost = -lost + 1. + ENDIF. + ENDMETHOD. + + METHOD assign_rank. + DATA lv_rank TYPE i. + DATA lv_position TYPE i. + DATA ls_previous TYPE ty_row. + + LOOP AT ct_table ASSIGNING FIELD-SYMBOL(). + lv_position = lv_position + 1. + IF lv_position = 1 + OR -points <> ls_previous-points + OR -goal_diff <> ls_previous-goal_diff + OR -goals_for <> ls_previous-goals_for. + lv_rank = lv_position. + ENDIF. + -rank = lv_rank. + ls_previous = . + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/reference/league_table.testclasses.abap b/tasks_gen/train/G1069/reference/league_table.testclasses.abap new file mode 100644 index 0000000..9321684 --- /dev/null +++ b/tasks_gen/train/G1069/reference/league_table.testclasses.abap @@ -0,0 +1,47 @@ +CLASS ltc_league_table DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}league_table. + + METHODS setup. + METHODS empty_input_is_empty FOR TESTING. + METHODS counts_points FOR TESTING. + METHODS ignores_invalid_match FOR TESTING. + METHODS ranks_ties_equal FOR TESTING. +ENDCLASS. + + +CLASS ltc_league_table IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}league_table( ). + ENDMETHOD. + + METHOD empty_input_is_empty. + DATA(lt_table) = mo_cut->evaluate( VALUE {{p}}league_table=>tt_match( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_table ) ). + ENDMETHOD. + + METHOD counts_points. + DATA(lt_table) = mo_cut->evaluate( VALUE {{p}}league_table=>tt_match( + ( home_team = 'A' away_team = 'B' home_goals = 2 away_goals = 0 ) + ( home_team = 'B' away_team = 'A' home_goals = 2 away_goals = 2 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_table ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_table[ 1 ]-team ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_table[ 1 ]-points ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 2 ]-points ). + ENDMETHOD. + + METHOD ignores_invalid_match. + DATA(lt_table) = mo_cut->evaluate( VALUE {{p}}league_table=>tt_match( + ( home_team = 'A' away_team = 'A' home_goals = 1 away_goals = 0 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_table ) ). + ENDMETHOD. + + METHOD ranks_ties_equal. + DATA(lt_table) = mo_cut->evaluate( VALUE {{p}}league_table=>tt_match( + ( home_team = 'A' away_team = 'B' home_goals = 1 away_goals = 1 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_table ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 1 ]-rank ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_table[ 2 ]-rank ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1069/spec.md b/tasks_gen/train/G1069/spec.md new file mode 100644 index 0000000..72a0247 --- /dev/null +++ b/tasks_gen/train/G1069/spec.md @@ -0,0 +1,67 @@ +# 1. Goal +A regional football league collects the results of one season. The league office +needs the league table. The table shows one row for each team with the matches, +the points, the goals and the rank. A league program calls the evaluation once +for the whole season. + +# 2. Open questions +None. + +# 3. Context +- Work in package $TMP. No other object is necessary. +- The caller collects all match results of the season in one internal table. + The caller passes this table to the evaluation. +- The evaluation works only on this table. It does not read the database. + +# 4. Contract +- Create the class {{P}}LEAGUE_TABLE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class has these public types: + - TY_TEAM: type c length 10. The name of a team. + - TY_MATCH: structure with HOME_TEAM (TY_TEAM), AWAY_TEAM (TY_TEAM), + HOME_GOALS (type i), AWAY_GOALS (type i). + - TT_MATCH: standard table of TY_MATCH. + - TY_ROW: structure with RANK (type i), TEAM (TY_TEAM), PLAYED (type i), + WON (type i), DRAWN (type i), LOST (type i), GOALS_FOR (type i), + GOALS_AGAINST (type i), GOAL_DIFF (type i), POINTS (type i). + - TT_ROW: standard table of TY_ROW. +- The class has this public method: + EVALUATE + IMPORTING it_matches TYPE tt_match + RETURNING VALUE(rt_table) TYPE tt_row. +- Do not add other public methods. + +# 5. Business rules +1. A match is valid when HOME_TEAM is not initial, AWAY_TEAM is not initial, + HOME_TEAM and AWAY_TEAM are different, HOME_GOALS is not negative and + AWAY_GOALS is not negative. Ignore every match that is not valid. +2. Each team that appears in at least one valid match gets exactly one row in + the result. A team that appears in several matches keeps this one row. +3. For each valid match the home team gets HOME_GOALS as goals for and + AWAY_GOALS as goals against. The away team gets AWAY_GOALS as goals for and + HOME_GOALS as goals against. +4. The team with more goals wins the match. If both teams have the same number + of goals, both teams draw the match. +5. A win gives 3 points. A draw gives 1 point. A loss gives 0 points. +6. PLAYED is the number of valid matches of the team. WON, DRAWN and LOST count + the wins, the draws and the losses. GOALS_FOR and GOALS_AGAINST are the sums + of the goals. GOAL_DIFF is GOALS_FOR minus GOALS_AGAINST. POINTS is the sum + of the points. +7. Sort the result: POINTS descending, then GOAL_DIFF descending, then + GOALS_FOR descending, then TEAM ascending. +8. RANK of a row is 1 plus the number of rows in the result that have more + POINTS, or that have the same POINTS and a higher GOAL_DIFF, or that have + the same POINTS, the same GOAL_DIFF and more GOALS_FOR. Rows with equal + POINTS, equal GOAL_DIFF and equal GOALS_FOR have the same rank. +9. If IT_MATCHES is empty or has no valid match, return an empty table. + +# 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: no database access. Do not create other objects in 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. diff --git a/tasks_gen/train/G1069/task.json b/tasks_gen/train/G1069/task.json new file mode 100644 index 0000000..ab58a98 --- /dev/null +++ b/tasks_gen/train/G1069/task.json @@ -0,0 +1,44 @@ +{ + "id": "G1069", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}LEAGUE_TABLE" + } + ], + "out_of_scope": [ + "No database access.", + "No other object in package $TMP." + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests for the league table" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}LEAGUE_TABLE", + "file": "reference/league_table.clas.abap", + "description": "League table builder", + "testclasses_file": "reference/league_table.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1070/faulty/m0_shred_pricer.clas.abap b/tasks_gen/train/G1070/faulty/m0_shred_pricer.clas.abap new file mode 100644 index 0000000..929fb4a --- /dev/null +++ b/tasks_gen/train/G1070/faulty/m0_shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes / box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service = c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes >= c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total * c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_true. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/faulty/m1_shred_pricer.clas.abap b/tasks_gen/train/G1070/faulty/m1_shred_pricer.clas.abap new file mode 100644 index 0000000..4c6bc4c --- /dev/null +++ b/tasks_gen/train/G1070/faulty/m1_shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes * box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service <> c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes >= c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total * c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_true. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/faulty/m2_shred_pricer.clas.abap b/tasks_gen/train/G1070/faulty/m2_shred_pricer.clas.abap new file mode 100644 index 0000000..a640918 --- /dev/null +++ b/tasks_gen/train/G1070/faulty/m2_shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes * box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service = c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes < c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total * c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_true. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/faulty/m3_shred_pricer.clas.abap b/tasks_gen/train/G1070/faulty/m3_shred_pricer.clas.abap new file mode 100644 index 0000000..f05cf05 --- /dev/null +++ b/tasks_gen/train/G1070/faulty/m3_shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes * box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service = c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes >= c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total / c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_true. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/faulty/m4_shred_pricer.clas.abap b/tasks_gen/train/G1070/faulty/m4_shred_pricer.clas.abap new file mode 100644 index 0000000..1b718a8 --- /dev/null +++ b/tasks_gen/train/G1070/faulty/m4_shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes * box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service = c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes >= c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total * c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_false. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/generation.json b/tasks_gen/train/G1070/generation.json new file mode 100644 index 0000000..75ac91a --- /dev/null +++ b/tasks_gen/train/G1070/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1070", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1070/hidden/shred_hidden.clas.abap b/tasks_gen/train/G1070/hidden/shred_hidden.clas.abap new file mode 100644 index 0000000..d2ec1a5 --- /dev/null +++ b/tasks_gen/train/G1070/hidden/shred_hidden.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}shred_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}shred_pricer. + + METHODS setup. + METHODS expect_price + IMPORTING iv_expected TYPE {{p}}shred_pricer=>ty_price + iv_boxes TYPE i + iv_size TYPE {{p}}shred_pricer=>ty_size + iv_service TYPE {{p}}shred_pricer=>ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool. + + METHODS zero_boxes_is_free FOR TESTING. + METHODS negative_boxes_free FOR TESTING. + METHODS plant_minimum_charge FOR TESTING. + METHODS onsite_minimum_charge FOR TESTING. + METHODS onsite_surcharge FOR TESTING. + METHODS no_discount_below_ten FOR TESTING. + METHODS mid_volume_discount FOR TESTING. + METHODS high_volume_discount FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS contract_discount FOR TESTING. + METHODS all_effects_combined FOR TESTING. + METHODS unknown_size_uses_medium FOR TESTING. +ENDCLASS. + + +CLASS {{p}}shred_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}shred_pricer( ). + ENDMETHOD. + + METHOD expect_price. + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_price( iv_boxes = iv_boxes + iv_size = iv_size + iv_service = iv_service + iv_express = iv_express + iv_contract = iv_contract ) ). + ENDMETHOD. + + METHOD zero_boxes_is_free. + expect_price( iv_expected = '0.00' iv_boxes = 0 iv_size = 120 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD negative_boxes_free. + expect_price( iv_expected = '0.00' iv_boxes = -3 iv_size = 60 + iv_service = 'O' iv_express = abap_true iv_contract = abap_true ). + ENDMETHOD. + + METHOD plant_minimum_charge. + expect_price( iv_expected = '35.00' iv_boxes = 1 iv_size = 60 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD onsite_minimum_charge. + expect_price( iv_expected = '60.00' iv_boxes = 1 iv_size = 60 + iv_service = 'O' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD onsite_surcharge. + expect_price( iv_expected = '107.50' iv_boxes = 5 iv_size = 240 + iv_service = 'O' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD no_discount_below_ten. + expect_price( iv_expected = '72.00' iv_boxes = 9 iv_size = 60 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD mid_volume_discount. + expect_price( iv_expected = '182.40' iv_boxes = 24 iv_size = 60 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD high_volume_discount. + expect_price( iv_expected = '270.00' iv_boxes = 25 iv_size = 120 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD express_surcharge. + expect_price( iv_expected = '205.20' iv_boxes = 10 iv_size = 240 + iv_service = 'P' iv_express = abap_true iv_contract = abap_false ). + ENDMETHOD. + + METHOD contract_discount. + expect_price( iv_expected = '153.90' iv_boxes = 10 iv_size = 240 + iv_service = 'P' iv_express = abap_false iv_contract = abap_true ). + ENDMETHOD. + + METHOD all_effects_combined. + expect_price( iv_expected = '437.40' iv_boxes = 25 iv_size = 240 + iv_service = 'P' iv_express = abap_true iv_contract = abap_true ). + ENDMETHOD. + + METHOD unknown_size_uses_medium. + expect_price( iv_expected = '114.00' iv_boxes = 10 iv_size = 100 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/mutation.json b/tasks_gen/train/G1070/mutation.json new file mode 100644 index 0000000..4cce723 --- /dev/null +++ b/tasks_gen/train/G1070/mutation.json @@ -0,0 +1,83 @@ +{ + "task": "G1070", + "mutants": [ + { + "object": "{{P}}SHRED_PRICER", + "mutant": "line 82: * -> / (arith)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "ALL_EFFECTS_COMBINED", + "CONTRACT_DISCOUNT", + "EXPRESS_SURCHARGE", + "HIGH_VOLUME_DISCOUNT", + "MID_VOLUME_DISCOUNT", + "NO_DISCOUNT_BELOW_TEN", + "ONSITE_SURCHARGE", + "UNKNOWN_SIZE_USES_MEDIUM" + ] + }, + { + "object": "{{P}}SHRED_PRICER", + "mutant": "line 104: = -> <> (eq)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "ALL_EFFECTS_COMBINED", + "CONTRACT_DISCOUNT", + "EXPRESS_SURCHARGE", + "HIGH_VOLUME_DISCOUNT", + "MID_VOLUME_DISCOUNT", + "NO_DISCOUNT_BELOW_TEN", + "ONSITE_SURCHARGE", + "UNKNOWN_SIZE_USES_MEDIUM" + ] + }, + { + "object": "{{P}}SHRED_PRICER", + "mutant": "line 110: >= -> < (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "ALL_EFFECTS_COMBINED", + "CONTRACT_DISCOUNT", + "EXPRESS_SURCHARGE", + "HIGH_VOLUME_DISCOUNT", + "MID_VOLUME_DISCOUNT", + "NO_DISCOUNT_BELOW_TEN", + "ONSITE_SURCHARGE", + "UNKNOWN_SIZE_USES_MEDIUM" + ] + }, + { + "object": "{{P}}SHRED_PRICER", + "mutant": "line 121: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "ALL_EFFECTS_COMBINED", + "CONTRACT_DISCOUNT" + ] + }, + { + "object": "{{P}}SHRED_PRICER", + "mutant": "line 128: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "ALL_EFFECTS_COMBINED", + "CONTRACT_DISCOUNT", + "EXPRESS_SURCHARGE", + "HIGH_VOLUME_DISCOUNT", + "MID_VOLUME_DISCOUNT", + "NO_DISCOUNT_BELOW_TEN", + "ONSITE_SURCHARGE", + "UNKNOWN_SIZE_USES_MEDIUM" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1070/reference/shred_pricer.clas.abap b/tasks_gen/train/G1070/reference/shred_pricer.clas.abap new file mode 100644 index 0000000..3bd88cc --- /dev/null +++ b/tasks_gen/train/G1070/reference/shred_pricer.clas.abap @@ -0,0 +1,152 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + + CONSTANTS: + c_price_small TYPE ty_price VALUE '8.00', + c_price_medium TYPE ty_price VALUE '12.00', + c_price_large TYPE ty_price VALUE '18.00', + c_onsite_surcharge TYPE ty_price VALUE '3.50', + c_express_factor TYPE ty_price VALUE '1.20', + c_contract_factor TYPE ty_price VALUE '0.90', + c_high_volume_factor TYPE ty_price VALUE '0.90', + c_mid_volume_factor TYPE ty_price VALUE '0.95', + c_mid_volume_limit TYPE i VALUE 10, + c_high_volume_limit TYPE i VALUE 25, + c_onsite_minimum TYPE ty_price VALUE '60.00', + c_plant_minimum TYPE ty_price VALUE '35.00'. + + METHODS box_price + IMPORTING iv_size TYPE ty_size + iv_service TYPE ty_service + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS apply_volume_discount + IMPORTING iv_boxes TYPE i + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_contract_discount + IMPORTING iv_contract TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_express_surcharge + IMPORTING iv_express TYPE abap_bool + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + + METHODS apply_minimum + IMPORTING iv_service TYPE ty_service + iv_total TYPE ty_price + RETURNING VALUE(rv_total) TYPE ty_price. + +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_total TYPE ty_price. + + IF iv_boxes <= 0. + RETURN. + ENDIF. + + lv_total = iv_boxes * box_price( iv_size = iv_size + iv_service = iv_service ). + lv_total = apply_volume_discount( iv_boxes = iv_boxes + iv_total = lv_total ). + lv_total = apply_contract_discount( iv_contract = iv_contract + iv_total = lv_total ). + lv_total = apply_express_surcharge( iv_express = iv_express + iv_total = lv_total ). + rv_price = apply_minimum( iv_service = iv_service + iv_total = lv_total ). + ENDMETHOD. + + METHOD box_price. + CASE iv_size. + WHEN c_size-small. + rv_price = c_price_small. + WHEN c_size-large. + rv_price = c_price_large. + WHEN OTHERS. + rv_price = c_price_medium. + ENDCASE. + + IF iv_service = c_service-onsite. + rv_price = rv_price + c_onsite_surcharge. + ENDIF. + ENDMETHOD. + + METHOD apply_volume_discount. + IF iv_boxes >= c_high_volume_limit. + rv_total = iv_total * c_high_volume_factor. + ELSEIF iv_boxes >= c_mid_volume_limit. + rv_total = iv_total * c_mid_volume_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_contract_discount. + IF iv_contract = abap_true. + rv_total = iv_total * c_contract_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_express_surcharge. + IF iv_express = abap_true. + rv_total = iv_total * c_express_factor. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + + METHOD apply_minimum. + DATA lv_minimum TYPE ty_price. + + CASE iv_service. + WHEN c_service-onsite. + lv_minimum = c_onsite_minimum. + WHEN OTHERS. + lv_minimum = c_plant_minimum. + ENDCASE. + + IF iv_total < lv_minimum. + rv_total = lv_minimum. + ELSE. + rv_total = iv_total. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/reference/shred_pricer.testclasses.abap b/tasks_gen/train/G1070/reference/shred_pricer.testclasses.abap new file mode 100644 index 0000000..e0f4d79 --- /dev/null +++ b/tasks_gen/train/G1070/reference/shred_pricer.testclasses.abap @@ -0,0 +1,71 @@ +CLASS ltc_shred_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}shred_pricer. + + METHODS setup. + METHODS expect_price + IMPORTING iv_expected TYPE {{p}}shred_pricer=>ty_price + iv_boxes TYPE i + iv_size TYPE {{p}}shred_pricer=>ty_size + iv_service TYPE {{p}}shred_pricer=>ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool. + + METHODS no_boxes_is_free FOR TESTING. + METHODS plant_minimum_charge FOR TESTING. + METHODS onsite_minimum_charge FOR TESTING. + METHODS volume_discount FOR TESTING. + METHODS contract_and_express FOR TESTING. + METHODS unknown_size_falls_back FOR TESTING. +ENDCLASS. + + +CLASS ltc_shred_pricer IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}shred_pricer( ). + ENDMETHOD. + + METHOD expect_price. + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_price( iv_boxes = iv_boxes + iv_size = iv_size + iv_service = iv_service + iv_express = iv_express + iv_contract = iv_contract ) ). + ENDMETHOD. + + METHOD no_boxes_is_free. + expect_price( iv_expected = '0.00' iv_boxes = 0 iv_size = 120 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD plant_minimum_charge. + expect_price( iv_expected = '35.00' iv_boxes = 1 iv_size = 60 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD onsite_minimum_charge. + expect_price( iv_expected = '60.00' iv_boxes = 1 iv_size = 60 + iv_service = 'O' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD volume_discount. + expect_price( iv_expected = '182.40' iv_boxes = 24 iv_size = 60 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + + METHOD contract_and_express. + expect_price( iv_expected = '437.40' iv_boxes = 25 iv_size = 240 + iv_service = 'P' iv_express = abap_true iv_contract = abap_true ). + ENDMETHOD. + + METHOD unknown_size_falls_back. + expect_price( iv_expected = '114.00' iv_boxes = 10 iv_size = 100 + iv_service = 'P' iv_express = abap_false iv_contract = abap_false ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/seed/shred_pricer.clas.abap b/tasks_gen/train/G1070/seed/shred_pricer.clas.abap new file mode 100644 index 0000000..8ad443a --- /dev/null +++ b/tasks_gen/train/G1070/seed/shred_pricer.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}shred_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_size TYPE i. + TYPES ty_service TYPE c LENGTH 1. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_size, + small TYPE ty_size VALUE 60, + large TYPE ty_size VALUE 240, + END OF c_size. + + CONSTANTS: + BEGIN OF c_service, + plant TYPE ty_service VALUE 'P', + onsite TYPE ty_service VALUE 'O', + END OF c_service. + + METHODS calculate_price + IMPORTING iv_boxes TYPE i + iv_size TYPE ty_size + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_contract TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}shred_pricer IMPLEMENTATION. + + METHOD calculate_price. + + DATA: lv_base TYPE ty_price, + lv_total TYPE ty_price, + lv_min TYPE ty_price, + lv_factor TYPE ty_price. + + rv_price = 0. + +* no boxes, no price + IF iv_boxes <= 0. + RETURN. + ENDIF. + +* price of one box + IF iv_size = 60. + lv_base = '8.00'. + ELSE. + IF iv_size = 120. + lv_base = '12.00'. + ELSE. + IF iv_size = 240. + lv_base = '18.00'. + ELSE. + lv_base = '12.00'. + ENDIF. + ENDIF. + ENDIF. + +* on site shredding costs more + IF iv_service = 'O'. + lv_base = lv_base + '3.50'. + ENDIF. + +* add one box after the other + lv_total = 0. + DO iv_boxes TIMES. + lv_total = lv_total + lv_base. + ENDDO. + +* discount for many boxes + IF iv_boxes >= 25. + lv_factor = '0.90'. + ELSE. + IF iv_boxes >= 10. + lv_factor = '0.95'. + ELSE. + lv_factor = '1.00'. + ENDIF. + ENDIF. + lv_total = lv_total * lv_factor. + +* contract customers get a discount + IF iv_contract = 'X'. + lv_total = lv_total * '0.90'. + ENDIF. + +* express service costs more + IF iv_express = 'X'. + lv_total = lv_total * '1.20'. + ENDIF. + +* minimum charge + IF iv_service = 'O'. + lv_min = '60.00'. + ELSE. + lv_min = '35.00'. + ENDIF. + IF lv_total < lv_min. + lv_total = lv_min. + ENDIF. + + rv_price = lv_total. + + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1070/spec.md b/tasks_gen/train/G1070/spec.md new file mode 100644 index 0000000..a14d393 --- /dev/null +++ b/tasks_gen/train/G1070/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A document shredding company prices a collection of paper boxes. The order desk +calls the class {{P}}SHRED_PRICER for each order. The class was written many years +ago and is hard to maintain. Refactor the class. The price of an order must stay +the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SHRED_PRICER exists in package $TMP. It is active. +- The class is public and final and has a public constructor without parameters. +- The order desk calls the public method CALCULATE_PRICE. +- The class holds the complete pricing logic of the company. Read the class. +- No other document describes the pricing rules. + +# 4. Contract +- Keep the class {{P}}SHRED_PRICER in package $TMP. Keep the name. +- Keep the class public and final with a public constructor without parameters. +- Keep the public types TY_SIZE, TY_SERVICE, and TY_PRICE. +- Keep the public constants C_SIZE (SMALL, LARGE) and C_SERVICE (PLANT, ONSITE). +- Keep the public method CALCULATE_PRICE with these parameters: + IV_BOXES TYPE i, IV_SIZE TYPE ty_size, IV_SERVICE TYPE ty_service, + IV_EXPRESS TYPE abap_bool, IV_CONTRACT TYPE abap_bool, and the returning + parameter RV_PRICE TYPE ty_price. +- Do not add other public methods. + +# 5. Business rules +- The behavior stays the same. For every order, CALCULATE_PRICE must return the + same price as the current class. +- The pricing rules are in the current class. Read the class and keep every rule, + including the price of a box, the surcharge for on-site shredding, the discount + for many boxes, the discount for contract customers, the surcharge for express + service, the fallback for an unknown box size, and the minimum charge. +- The sequence of the rules must not change. +- Quality goals: + - Short methods. One method does one thing. + - No magic numbers. Use named constants. + - The class must be easy to read and easy to change. + - The public interface must stay the same. + +# 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 the public interface of {{P}}SHRED_PRICER. Do not + change the package. + +# 7. Acceptance +- The class {{P}}SHRED_PRICER is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1070/task.json b/tasks_gen/train/G1070/task.json new file mode 100644 index 0000000..c6bf6c8 --- /dev/null +++ b/tasks_gen/train/G1070/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1070", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}SHRED_PRICER", + "file": "seed/shred_pricer.clas.abap", + "description": "Legacy class that prices a document shredding order" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SHRED_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SHRED_HIDDEN", + "file": "hidden/shred_hidden.clas.abap", + "description": "Hidden tests for the shredding price" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SHRED_PRICER", + "file": "reference/shred_pricer.clas.abap", + "description": "Refactored shredding price calculation", + "testclasses_file": "reference/shred_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1071/faulty/m0_i_dive_stats.ddls.asddls b/tasks_gen/train/G1071/faulty/m0_i_dive_stats.ddls.asddls new file mode 100644 index 0000000..cb1f6b8 --- /dev/null +++ b/tasks_gen/train/G1071/faulty/m0_i_dive_stats.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Dive stats' +define view entity {{P}}I_DIVE_STATS + as select from {{p}}diver as diver + left outer join {{p}}dive as dive on dive.diver_id = diver.diver_id + left outer join {{p}}site as site on site.site_id = dive.site_id +{ + key diver.diver_id as diver_id, + diver.diver_name as diver_name, + count( * ) as dive_count, + max( site.depth_m ) as deepest_m +} +group by + diver.diver_id, + diver.diver_name diff --git a/tasks_gen/train/G1071/faulty/m1_i_dive_stats.ddls.asddls b/tasks_gen/train/G1071/faulty/m1_i_dive_stats.ddls.asddls new file mode 100644 index 0000000..737a7fd --- /dev/null +++ b/tasks_gen/train/G1071/faulty/m1_i_dive_stats.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Dive stats' +define view entity {{P}}I_DIVE_STATS + as select from {{p}}diver as diver + inner join {{p}}dive as dive on dive.diver_id <> diver.diver_id + left outer join {{p}}site as site on site.site_id = dive.site_id +{ + key diver.diver_id as diver_id, + diver.diver_name as diver_name, + count( * ) as dive_count, + max( site.depth_m ) as deepest_m +} +group by + diver.diver_id, + diver.diver_name diff --git a/tasks_gen/train/G1071/faulty/m2_i_dive_stats.ddls.asddls b/tasks_gen/train/G1071/faulty/m2_i_dive_stats.ddls.asddls new file mode 100644 index 0000000..bb087cf --- /dev/null +++ b/tasks_gen/train/G1071/faulty/m2_i_dive_stats.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Dive stats' +define view entity {{P}}I_DIVE_STATS + as select from {{p}}diver as diver + inner join {{p}}dive as dive on dive.diver_id = diver.diver_id + left outer join {{p}}site as site on site.site_id <> dive.site_id +{ + key diver.diver_id as diver_id, + diver.diver_name as diver_name, + count( * ) as dive_count, + max( site.depth_m ) as deepest_m +} +group by + diver.diver_id, + diver.diver_name diff --git a/tasks_gen/train/G1071/generation.json b/tasks_gen/train/G1071/generation.json new file mode 100644 index 0000000..7520340 --- /dev/null +++ b/tasks_gen/train/G1071/generation.json @@ -0,0 +1,40 @@ +{ + "id": "G1071", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task T15 (similarity spec 0.77, rules 0.63, names 0.00). Choose a different business topic and different object names.", + "Too close to task G0018 (similarity spec 0.78, rules 0.64, names 0.00). Choose a different business topic and different object names.", + "Too close to task T15 (similarity spec 0.77, rules 0.63, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1071/hidden/t_dive_stats_hidden.clas.abap b/tasks_gen/train/G1071/hidden/t_dive_stats_hidden.clas.abap new file mode 100644 index 0000000..50c6220 --- /dev/null +++ b/tasks_gen/train/G1071/hidden/t_dive_stats_hidden.clas.abap @@ -0,0 +1,78 @@ +CLASS {{p}}t_dive_stats_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS one_row_per_diver FOR TESTING. + METHODS dive_count_all_dives FOR TESTING. + METHODS deepest_of_diver FOR TESTING. + METHODS diver_without_dives FOR TESTING. + METHODS dive_without_site_counts FOR TESTING. + METHODS diver_name_shown FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_dive_stats_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_DIVE_STATS' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_diver TYPE STANDARD TABLE OF {{p}}diver WITH EMPTY KEY. + DATA lt_site TYPE STANDARD TABLE OF {{p}}site WITH EMPTY KEY. + DATA lt_dive TYPE STANDARD TABLE OF {{p}}dive WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_diver = VALUE #( + ( diver_id = 'D1' diver_name = 'Alice' ) + ( diver_id = 'D2' diver_name = 'Bob' ) + ( diver_id = 'D3' diver_name = 'Carol' ) ). + lt_site = VALUE #( + ( site_id = 'S1' site_name = 'Reef' depth_m = 10 ) + ( site_id = 'S2' site_name = 'Wreck' depth_m = 30 ) + ( site_id = 'S3' site_name = 'Cave' depth_m = 20 ) ). + lt_dive = VALUE #( + ( dive_id = 'V1' diver_id = 'D1' site_id = 'S1' dive_date = '20250101' ) + ( dive_id = 'V2' diver_id = 'D1' site_id = 'S2' dive_date = '20250102' ) + ( dive_id = 'V3' diver_id = 'D1' site_id = 'S3' dive_date = '20250103' ) + ( dive_id = 'V4' diver_id = 'D2' site_id = 'S1' dive_date = '20250104' ) + ( dive_id = 'V5' diver_id = 'D2' site_id = 'S9' dive_date = '20250105' ) ). + go_env->insert_test_data( lt_diver ). + go_env->insert_test_data( lt_site ). + go_env->insert_test_data( lt_dive ). + ENDMETHOD. + + METHOD one_row_per_diver. + SELECT COUNT(*) FROM {{p}}i_dive_stats INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD dive_count_all_dives. + SELECT SINGLE dive_count FROM {{p}}i_dive_stats WHERE diver_id = 'D1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD deepest_of_diver. + SELECT SINGLE deepest_m FROM {{p}}i_dive_stats WHERE diver_id = 'D1' INTO @DATA(lv_depth). + cl_abap_unit_assert=>assert_equals( exp = 30 act = lv_depth ). + ENDMETHOD. + + METHOD diver_without_dives. + SELECT COUNT(*) FROM {{p}}i_dive_stats WHERE diver_id = 'D3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD dive_without_site_counts. + SELECT SINGLE dive_count FROM {{p}}i_dive_stats WHERE diver_id = 'D2' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD diver_name_shown. + SELECT SINGLE diver_name FROM {{p}}i_dive_stats WHERE diver_id = 'D1' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'Alice' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1071/mutation.json b/tasks_gen/train/G1071/mutation.json new file mode 100644 index 0000000..d91d0a7 --- /dev/null +++ b/tasks_gen/train/G1071/mutation.json @@ -0,0 +1,42 @@ +{ + "task": "G1071", + "mutants": [ + { + "object": "{{P}}I_DIVE_STATS", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "DIVER_WITHOUT_DIVES", + "ONE_ROW_PER_DIVER" + ] + }, + { + "object": "{{P}}I_DIVE_STATS", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "DEEPEST_OF_DIVER", + "DIVER_WITHOUT_DIVES", + "DIVE_COUNT_ALL_DIVES", + "DIVE_WITHOUT_SITE_COUNTS", + "ONE_ROW_PER_DIVER" + ] + }, + { + "object": "{{P}}I_DIVE_STATS", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "DIVE_COUNT_ALL_DIVES", + "DIVE_WITHOUT_SITE_COUNTS" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1071/reference/i_dive_stats.ddls.asddls b/tasks_gen/train/G1071/reference/i_dive_stats.ddls.asddls new file mode 100644 index 0000000..6df6795 --- /dev/null +++ b/tasks_gen/train/G1071/reference/i_dive_stats.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Dive stats' +define view entity {{P}}I_DIVE_STATS + as select from {{p}}diver as diver + inner join {{p}}dive as dive on dive.diver_id = diver.diver_id + left outer join {{p}}site as site on site.site_id = dive.site_id +{ + key diver.diver_id as diver_id, + diver.diver_name as diver_name, + count( * ) as dive_count, + max( site.depth_m ) as deepest_m +} +group by + diver.diver_id, + diver.diver_name diff --git a/tasks_gen/train/G1071/reference/t_dive_stats_test.clas.abap b/tasks_gen/train/G1071/reference/t_dive_stats_test.clas.abap new file mode 100644 index 0000000..f211598 --- /dev/null +++ b/tasks_gen/train/G1071/reference/t_dive_stats_test.clas.abap @@ -0,0 +1,41 @@ +CLASS {{p}}t_dive_stats_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS stats_of_one_diver FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_dive_stats_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_DIVE_STATS' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_diver TYPE STANDARD TABLE OF {{p}}diver WITH EMPTY KEY. + DATA lt_site TYPE STANDARD TABLE OF {{p}}site WITH EMPTY KEY. + DATA lt_dive TYPE STANDARD TABLE OF {{p}}dive WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_diver = VALUE #( ( diver_id = 'X1' diver_name = 'Test' ) ). + lt_site = VALUE #( ( site_id = 'S1' site_name = 'Reef' depth_m = 12 ) ). + lt_dive = VALUE #( + ( dive_id = 'V1' diver_id = 'X1' site_id = 'S1' dive_date = '20250101' ) + ( dive_id = 'V2' diver_id = 'X1' site_id = 'S1' dive_date = '20250102' ) ). + go_env->insert_test_data( lt_diver ). + go_env->insert_test_data( lt_site ). + go_env->insert_test_data( lt_dive ). + ENDMETHOD. + + METHOD stats_of_one_diver. + SELECT SINGLE dive_count, deepest_m FROM {{p}}i_dive_stats WHERE diver_id = 'X1' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-dive_count ). + cl_abap_unit_assert=>assert_equals( exp = 12 act = ls_row-deepest_m ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1071/seed/dive.tabl.asabap b/tasks_gen/train/G1071/seed/dive.tabl.asabap new file mode 100644 index 0000000..daf5c24 --- /dev/null +++ b/tasks_gen/train/G1071/seed/dive.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Dive' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}dive { + key client : abap.clnt not null; + key dive_id : abap.char(10) not null; + diver_id : abap.char(10); + site_id : abap.char(10); + dive_date : abap.dats; +} diff --git a/tasks_gen/train/G1071/seed/diver.tabl.asabap b/tasks_gen/train/G1071/seed/diver.tabl.asabap new file mode 100644 index 0000000..8a3105f --- /dev/null +++ b/tasks_gen/train/G1071/seed/diver.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Diver' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}diver { + key client : abap.clnt not null; + key diver_id : abap.char(10) not null; + diver_name : abap.char(40); +} diff --git a/tasks_gen/train/G1071/seed/i_dive_stats.ddls.asddls b/tasks_gen/train/G1071/seed/i_dive_stats.ddls.asddls new file mode 100644 index 0000000..c0207a2 --- /dev/null +++ b/tasks_gen/train/G1071/seed/i_dive_stats.ddls.asddls @@ -0,0 +1,19 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Dive stats' +define view entity {{P}}I_DIVE_STATS + as select from {{p}}diver as diver + inner join {{p}}dive as dive on dive.diver_id = diver.diver_id + left outer join {{p}}site as site on site.site_id = dive.site_id +{ + key diver.diver_id as diver_id, + diver.diver_name as diver_name, + dive.dive_id as dive_id, + site.site_id as site_id, + count( * ) as dive_count, + max( site.depth_m ) as deepest_m +} +group by + diver.diver_id, + diver.diver_name, + dive.dive_id, + site.site_id diff --git a/tasks_gen/train/G1071/seed/seed_dive.clas.abap b/tasks_gen/train/G1071/seed/seed_dive.clas.abap new file mode 100644 index 0000000..d1573a3 --- /dev/null +++ b/tasks_gen/train/G1071/seed/seed_dive.clas.abap @@ -0,0 +1,34 @@ +CLASS {{p}}seed_dive DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS {{p}}seed_dive IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}diver. + DELETE FROM {{p}}site. + DELETE FROM {{p}}dive. + + INSERT {{p}}diver FROM TABLE @( VALUE #( + ( diver_id = 'D1' diver_name = 'Alice' ) + ( diver_id = 'D2' diver_name = 'Bob' ) + ( diver_id = 'D3' diver_name = 'Carol' ) ) ). + + INSERT {{p}}site FROM TABLE @( VALUE #( + ( site_id = 'S1' site_name = 'Reef' depth_m = 10 ) + ( site_id = 'S2' site_name = 'Wreck' depth_m = 30 ) + ( site_id = 'S3' site_name = 'Cave' depth_m = 20 ) ) ). + + INSERT {{p}}dive FROM TABLE @( VALUE #( + ( dive_id = 'V1' diver_id = 'D1' site_id = 'S1' dive_date = '20250101' ) + ( dive_id = 'V2' diver_id = 'D1' site_id = 'S2' dive_date = '20250102' ) + ( dive_id = 'V3' diver_id = 'D1' site_id = 'S3' dive_date = '20250103' ) + ( dive_id = 'V4' diver_id = 'D2' site_id = 'S1' dive_date = '20250104' ) + ( dive_id = 'V5' diver_id = 'D2' site_id = 'S9' dive_date = '20250105' ) ) ). + + SELECT * FROM {{p}}i_dive_stats INTO TABLE @DATA(lt_result). + out->write( lt_result ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1071/seed/site.tabl.asabap b/tasks_gen/train/G1071/seed/site.tabl.asabap new file mode 100644 index 0000000..97ae392 --- /dev/null +++ b/tasks_gen/train/G1071/seed/site.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Dive site' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}site { + key client : abap.clnt not null; + key site_id : abap.char(10) not null; + site_name : abap.char(40); + depth_m : abap.int4; +} diff --git a/tasks_gen/train/G1071/spec.md b/tasks_gen/train/G1071/spec.md new file mode 100644 index 0000000..65f6783 --- /dev/null +++ b/tasks_gen/train/G1071/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Fix the CDS view entity {{P}}I_DIVE_STATS in package $TMP. The view shows the +diving activity of each diver. The operations team reports two symptoms: +- The view shows one row for each dive of a diver, not one row for each diver. +- DIVE_COUNT is always 1. +The view must show one row for each diver, and DIVE_COUNT must show the number of +dives of the diver. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DIVER (diver), package $TMP. Key: DIVER_ID (CHAR 10). + Field: DIVER_NAME (CHAR 40). +- Table {{P}}SITE (dive site), package $TMP. Key: SITE_ID (CHAR 10). + Fields: SITE_NAME (CHAR 40), DEPTH_M (INT4, depth of the site in metres). +- Table {{P}}DIVE (dive), package $TMP. Key: DIVE_ID (CHAR 10). + Fields: DIVER_ID (CHAR 10), SITE_ID (CHAR 10), DIVE_DATE (DATS). +- The CDS view entity {{P}}I_DIVE_STATS exists in package $TMP and is active. +- The class {{P}}SEED_DIVE fills the three tables with test data and shows the + view. + +# 4. Contract +- Fix the CDS view entity {{P}}I_DIVE_STATS in package $TMP. +- Elements, with these names: DIVER_ID (key), DIVER_NAME, DIVE_COUNT, DEEPEST_M. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each diver that has at least one dive. A diver + without dives does not appear. +2. DIVE_COUNT is the number of dives of the diver. A dive whose site is not in + {{P}}SITE still counts as one dive. +3. DEEPEST_M is the greatest DEPTH_M of the sites of the dives of the diver. A + dive without a site adds nothing to DEEPEST_M. +4. DIVER_NAME is the name of the diver. +5. All other behavior stays the same. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Out of scope: do not change {{P}}DIVER, {{P}}SITE, {{P}}DIVE and + {{P}}SEED_DIVE. +- Coding standards: Clean ABAP. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1071/task.json b/tasks_gen/train/G1071/task.json new file mode 100644 index 0000000..e680857 --- /dev/null +++ b/tasks_gen/train/G1071/task.json @@ -0,0 +1,86 @@ +{ + "id": "G1071", + "category": "I", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DIVER", + "file": "seed/diver.tabl.asabap", + "description": "Diver" + }, + { + "type": "TABL", + "name": "{{P}}SITE", + "file": "seed/site.tabl.asabap", + "description": "Dive site" + }, + { + "type": "TABL", + "name": "{{P}}DIVE", + "file": "seed/dive.tabl.asabap", + "description": "Dive" + }, + { + "type": "DDLS", + "name": "{{P}}I_DIVE_STATS", + "file": "seed/i_dive_stats.ddls.asddls", + "description": "Faulty dive statistics view" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DIVE", + "file": "seed/seed_dive.clas.abap", + "description": "Seed data and show view", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_DIVE_STATS", + "fields": [ + "DIVER_ID", + "DIVER_NAME", + "DIVE_COUNT", + "DEEPEST_M" + ] + } + ], + "out_of_scope": [ + "{{P}}DIVER", + "{{P}}SITE", + "{{P}}DIVE", + "{{P}}SEED_DIVE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_DIVE_STATS_HIDDEN", + "file": "hidden/t_dive_stats_hidden.clas.abap", + "description": "Hidden tests for dive statistics" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_DIVE_STATS", + "file": "reference/i_dive_stats.ddls.asddls", + "description": "Fixed dive statistics view" + }, + { + "type": "CLAS", + "name": "{{P}}T_DIVE_STATS_TEST", + "file": "reference/t_dive_stats_test.clas.abap", + "description": "Reference tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1072/faulty/m1_i_guest_stay.ddls.asddls b/tasks_gen/train/G1072/faulty/m1_i_guest_stay.ddls.asddls new file mode 100644 index 0000000..bc310f1 --- /dev/null +++ b/tasks_gen/train/G1072/faulty/m1_i_guest_stay.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Guest stay totals' +define view entity {{P}}I_GUEST_STAY + as select from {{p}}guest as g + inner join {{p}}stay as s on s.guest_id = g.guest_id +{ + key g.guest_id as guest_id, + g.guest_name as guest_name, + count( * ) as stay_count, + max( cast( s.nights as abap.int8 ) ) as night_count, + sum( cast( s.nights as abap.dec( 10, 0 ) ) * s.rate ) as total_charge +} +group by + g.guest_id, + g.guest_name +having + count( * ) > 1 diff --git a/tasks_gen/train/G1072/faulty/m2_i_guest_stay.ddls.asddls b/tasks_gen/train/G1072/faulty/m2_i_guest_stay.ddls.asddls new file mode 100644 index 0000000..f3a2367 --- /dev/null +++ b/tasks_gen/train/G1072/faulty/m2_i_guest_stay.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Guest stay totals' +define view entity {{P}}I_GUEST_STAY + as select from {{p}}guest as g + inner join {{p}}stay as s on s.guest_id = g.guest_id +{ + key g.guest_id as guest_id, + g.guest_name as guest_name, + count( * ) as stay_count, + sum( cast( s.nights as abap.int8 ) ) as night_count, + max( cast( s.nights as abap.dec( 10, 0 ) ) * s.rate ) as total_charge +} +group by + g.guest_id, + g.guest_name +having + count( * ) > 1 diff --git a/tasks_gen/train/G1072/faulty/m3_i_guest_stay.ddls.asddls b/tasks_gen/train/G1072/faulty/m3_i_guest_stay.ddls.asddls new file mode 100644 index 0000000..9667c71 --- /dev/null +++ b/tasks_gen/train/G1072/faulty/m3_i_guest_stay.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Guest stay totals' +define view entity {{P}}I_GUEST_STAY + as select from {{p}}guest as g + inner join {{p}}stay as s on s.guest_id = g.guest_id +{ + key g.guest_id as guest_id, + g.guest_name as guest_name, + count( * ) as stay_count, + sum( cast( s.nights as abap.int8 ) ) as night_count, + sum( cast( s.nights as abap.dec( 10, 0 ) ) * s.rate ) as total_charge +} +group by + g.guest_id, + g.guest_name +having + count( * ) <= 1 diff --git a/tasks_gen/train/G1072/faulty/m4_i_guest_stay.ddls.asddls b/tasks_gen/train/G1072/faulty/m4_i_guest_stay.ddls.asddls new file mode 100644 index 0000000..3b31e09 --- /dev/null +++ b/tasks_gen/train/G1072/faulty/m4_i_guest_stay.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Guest stay totals' +define view entity {{P}}I_GUEST_STAY + as select from {{p}}guest as g + inner join {{p}}stay as s on s.guest_id = g.guest_id +{ + key g.guest_id as guest_id, + g.guest_name as guest_name, + count( * ) as stay_count, + sum( cast( s.nights as abap.int8 ) ) as night_count, + sum( cast( s.nights as abap.dec( 10, 0 ) ) * s.rate ) as total_charge +} +group by + g.guest_id, + g.guest_name +having + count( * ) > 2 diff --git a/tasks_gen/train/G1072/generation.json b/tasks_gen/train/G1072/generation.json new file mode 100644 index 0000000..1af81e1 --- /dev/null +++ b/tasks_gen/train/G1072/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1072", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1072/hidden/t30_hidden.clas.abap b/tasks_gen/train/G1072/hidden/t30_hidden.clas.abap new file mode 100644 index 0000000..4ac8d22 --- /dev/null +++ b/tasks_gen/train/G1072/hidden/t30_hidden.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}t30_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS only_repeat_guests FOR TESTING. + METHODS one_stay_guest_absent FOR TESTING. + METHODS no_stay_guest_absent FOR TESTING. + METHODS stay_counts FOR TESTING. + METHODS night_totals FOR TESTING. + METHODS total_charges FOR TESTING. + METHODS guest_names FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t30_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_GUEST_STAY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_guest TYPE STANDARD TABLE OF {{p}}guest WITH EMPTY KEY. + DATA lt_stay TYPE STANDARD TABLE OF {{p}}stay WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_guest = VALUE #( ( guest_id = 'G1' guest_name = 'ALPHA' ) + ( guest_id = 'G2' guest_name = 'BETA' ) + ( guest_id = 'G3' guest_name = 'GAMMA' ) + ( guest_id = 'G4' guest_name = 'DELTA' ) ). + lt_stay = VALUE #( ( stay_id = 'S1' guest_id = 'G1' hotel = 'H1' nights = 3 rate = '100.00' ) + ( stay_id = 'S2' guest_id = 'G1' hotel = 'H2' nights = 2 rate = '150.50' ) + ( stay_id = 'S3' guest_id = 'G2' hotel = 'H1' nights = 1 rate = '80.00' ) + ( stay_id = 'S4' guest_id = 'G3' hotel = 'H1' nights = 4 rate = '50.00' ) + ( stay_id = 'S5' guest_id = 'G3' hotel = 'H2' nights = 1 rate = '25.00' ) + ( stay_id = 'S6' guest_id = 'G3' hotel = 'H3' nights = 2 rate = '10.00' ) ). + go_env->insert_test_data( lt_guest ). + go_env->insert_test_data( lt_stay ). + ENDMETHOD. + + METHOD only_repeat_guests. + SELECT COUNT(*) FROM {{p}}i_guest_stay INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD one_stay_guest_absent. + SELECT COUNT(*) FROM {{p}}i_guest_stay WHERE guest_id = 'G2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD no_stay_guest_absent. + SELECT COUNT(*) FROM {{p}}i_guest_stay WHERE guest_id = 'G4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD stay_counts. + SELECT SINGLE stay_count FROM {{p}}i_guest_stay WHERE guest_id = 'G1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + SELECT SINGLE stay_count FROM {{p}}i_guest_stay WHERE guest_id = 'G3' INTO @DATA(lv_count3). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count3 ). + ENDMETHOD. + + METHOD night_totals. + SELECT SINGLE night_count FROM {{p}}i_guest_stay WHERE guest_id = 'G1' INTO @DATA(lv_nights). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) + act = CONV decfloat34( lv_nights ) ). + SELECT SINGLE night_count FROM {{p}}i_guest_stay WHERE guest_id = 'G3' INTO @DATA(lv_nights3). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) + act = CONV decfloat34( lv_nights3 ) ). + ENDMETHOD. + + METHOD total_charges. + SELECT SINGLE total_charge FROM {{p}}i_guest_stay WHERE guest_id = 'G1' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '601.00' ) + act = CONV decfloat34( lv_total ) ). + SELECT SINGLE total_charge FROM {{p}}i_guest_stay WHERE guest_id = 'G3' INTO @DATA(lv_total3). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '245.00' ) + act = CONV decfloat34( lv_total3 ) ). + ENDMETHOD. + + METHOD guest_names. + SELECT SINGLE guest_name FROM {{p}}i_guest_stay WHERE guest_id = 'G3' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'GAMMA' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1072/mutation.json b/tasks_gen/train/G1072/mutation.json new file mode 100644 index 0000000..0a04c1e --- /dev/null +++ b/tasks_gen/train/G1072/mutation.json @@ -0,0 +1,60 @@ +{ + "task": "G1072", + "mutants": [ + { + "object": "{{P}}I_GUEST_STAY", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "7/7", + "failed_tests": [] + }, + { + "object": "{{P}}I_GUEST_STAY", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "NIGHT_TOTALS" + ] + }, + { + "object": "{{P}}I_GUEST_STAY", + "mutant": "line 11: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "TOTAL_CHARGES" + ] + }, + { + "object": "{{P}}I_GUEST_STAY", + "mutant": "line 17: > -> <= (rel)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "GUEST_NAMES", + "NIGHT_TOTALS", + "ONE_STAY_GUEST_ABSENT", + "ONLY_REPEAT_GUESTS", + "STAY_COUNTS", + "TOTAL_CHARGES" + ] + }, + { + "object": "{{P}}I_GUEST_STAY", + "mutant": "line 17: 1 -> 2 (const)", + "status": "killed", + "hidden": "3/7", + "failed_tests": [ + "NIGHT_TOTALS", + "ONLY_REPEAT_GUESTS", + "STAY_COUNTS", + "TOTAL_CHARGES" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1072/reference/i_guest_stay.ddls.asddls b/tasks_gen/train/G1072/reference/i_guest_stay.ddls.asddls new file mode 100644 index 0000000..0716955 --- /dev/null +++ b/tasks_gen/train/G1072/reference/i_guest_stay.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Guest stay totals' +define view entity {{P}}I_GUEST_STAY + as select from {{p}}guest as g + inner join {{p}}stay as s on s.guest_id = g.guest_id +{ + key g.guest_id as guest_id, + g.guest_name as guest_name, + count( * ) as stay_count, + sum( cast( s.nights as abap.int8 ) ) as night_count, + sum( cast( s.nights as abap.dec( 10, 0 ) ) * s.rate ) as total_charge +} +group by + g.guest_id, + g.guest_name +having + count( * ) > 1 diff --git a/tasks_gen/train/G1072/reference/t30_test.clas.abap b/tasks_gen/train/G1072/reference/t30_test.clas.abap new file mode 100644 index 0000000..1e391df --- /dev/null +++ b/tasks_gen/train/G1072/reference/t30_test.clas.abap @@ -0,0 +1,45 @@ +CLASS {{p}}t30_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS totals_of_repeat_guest FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t30_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_GUEST_STAY' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_guest TYPE STANDARD TABLE OF {{p}}guest WITH EMPTY KEY. + DATA lt_stay TYPE STANDARD TABLE OF {{p}}stay WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_guest = VALUE #( ( guest_id = 'A' guest_name = 'ANNA' ) + ( guest_id = 'B' guest_name = 'BEN' ) ). + lt_stay = VALUE #( ( stay_id = '1' guest_id = 'A' hotel = 'H1' nights = 2 rate = '10.00' ) + ( stay_id = '2' guest_id = 'A' hotel = 'H2' nights = 3 rate = '20.00' ) + ( stay_id = '3' guest_id = 'B' hotel = 'H1' nights = 1 rate = '5.00' ) ). + go_env->insert_test_data( lt_guest ). + go_env->insert_test_data( lt_stay ). + ENDMETHOD. + + METHOD totals_of_repeat_guest. + SELECT SINGLE stay_count, night_count, total_charge FROM {{p}}i_guest_stay + WHERE guest_id = 'A' INTO @DATA(ls_total). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_total-stay_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) + act = CONV decfloat34( ls_total-night_count ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 80 ) + act = CONV decfloat34( ls_total-total_charge ) ) . + SELECT COUNT(*) FROM {{p}}i_guest_stay INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1072/seed/guest.tabl.asabap b/tasks_gen/train/G1072/seed/guest.tabl.asabap new file mode 100644 index 0000000..2c27bb2 --- /dev/null +++ b/tasks_gen/train/G1072/seed/guest.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Guest master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}guest { + key client : abap.clnt not null; + key guest_id : abap.char(10) not null; + guest_name : abap.char(30); +} diff --git a/tasks_gen/train/G1072/seed/stay.tabl.asabap b/tasks_gen/train/G1072/seed/stay.tabl.asabap new file mode 100644 index 0000000..36fff97 --- /dev/null +++ b/tasks_gen/train/G1072/seed/stay.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Hotel stay' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}stay { + key client : abap.clnt not null; + key stay_id : abap.char(10) not null; + guest_id : abap.char(10); + hotel : abap.char(4); + nights : abap.int4; + rate : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1072/spec.md b/tasks_gen/train/G1072/spec.md new file mode 100644 index 0000000..6d5c67e --- /dev/null +++ b/tasks_gen/train/G1072/spec.md @@ -0,0 +1,39 @@ +# 1. Goal +Show the stay totals of each guest of a hotel. The loyalty programme of the hotel +reads these totals, so the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}GUEST (guest master), package $TMP. Key: GUEST_ID (CHAR 10). + Field: GUEST_NAME (CHAR 30). +- Table {{P}}STAY (hotel stay), package $TMP. Key: STAY_ID (CHAR 10). + Fields: GUEST_ID (CHAR 10), HOTEL (CHAR 4), NIGHTS (INT4), RATE (DEC 15,2, + price for one night). + +# 4. Contract +- Create the CDS view entity {{P}}I_GUEST_STAY in package $TMP. +- Elements, with these names: GUEST_ID (key), GUEST_NAME, STAY_COUNT, + NIGHT_COUNT, TOTAL_CHARGE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each guest that has at least one stay in {{P}}STAY. Do not + show a guest without a stay. +2. Show only guests that have more than one stay. Do not show a guest with + exactly one stay. +3. STAY_COUNT is the number of stays of the guest. +4. NIGHT_COUNT is the sum of NIGHTS of all stays of the guest. +5. TOTAL_CHARGE is the sum of NIGHTS multiplied by RATE of all stays of the + guest, with 2 decimals. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}GUEST and {{P}}STAY. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. + +# 7. Acceptance +- The view {{P}}I_GUEST_STAY is active and has no syntax error. +- The hidden tests pass. diff --git a/tasks_gen/train/G1072/task.json b/tasks_gen/train/G1072/task.json new file mode 100644 index 0000000..e8c7f4f --- /dev/null +++ b/tasks_gen/train/G1072/task.json @@ -0,0 +1,66 @@ +{ + "id": "G1072", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}GUEST", + "file": "seed/guest.tabl.asabap", + "description": "Guest master" + }, + { + "type": "TABL", + "name": "{{P}}STAY", + "file": "seed/stay.tabl.asabap", + "description": "Hotel stay" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_GUEST_STAY", + "fields": [ + "GUEST_ID", + "GUEST_NAME", + "STAY_COUNT", + "NIGHT_COUNT", + "TOTAL_CHARGE" + ] + } + ], + "out_of_scope": [ + "{{P}}GUEST", + "{{P}}STAY" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T30_HIDDEN", + "file": "hidden/t30_hidden.clas.abap", + "description": "T30 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_GUEST_STAY", + "file": "reference/i_guest_stay.ddls.asddls", + "description": "Guest stay totals" + }, + { + "type": "CLAS", + "name": "{{P}}T30_TEST", + "file": "reference/t30_test.clas.abap", + "description": "T30 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1073/generation.json b/tasks_gen/train/G1073/generation.json new file mode 100644 index 0000000..e7494a0 --- /dev/null +++ b/tasks_gen/train/G1073/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1073", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/parking_eval.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/parking_eval.clas.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1073/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1073/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..6e9a937 --- /dev/null +++ b/tasks_gen/train/G1073/hidden/t31_hidden.clas.abap @@ -0,0 +1,151 @@ +CLASS {{p}}t31_hidden DEFINITION + PUBLIC + FINAL + FOR TESTING + RISK LEVEL HARMLESS + DURATION SHORT + CREATE PUBLIC. + PRIVATE SECTION. + CONSTANTS: + c_fee_000 TYPE p LENGTH 8 DECIMALS 2 VALUE '0.00', + c_fee_200 TYPE p LENGTH 8 DECIMALS 2 VALUE '2.00', + c_fee_350 TYPE p LENGTH 8 DECIMALS 2 VALUE '3.50', + c_fee_400 TYPE p LENGTH 8 DECIMALS 2 VALUE '4.00', + c_fee_500 TYPE p LENGTH 8 DECIMALS 2 VALUE '5.00', + c_fee_1000 TYPE p LENGTH 8 DECIMALS 2 VALUE '10.00', + c_fee_1250 TYPE p LENGTH 8 DECIMALS 2 VALUE '12.50', + c_fee_2000 TYPE p LENGTH 8 DECIMALS 2 VALUE '20.00'. + + DATA mo_cut TYPE REF TO {{p}}if_park_eval. + + METHODS setup. + "! @testing {{p}}parking_eval + METHODS empty_input FOR TESTING. + "! @testing {{p}}parking_eval + METHODS free_minutes FOR TESTING. + "! @testing {{p}}parking_eval + METHODS one_started_hour FOR TESTING. + "! @testing {{p}}parking_eval + METHODS two_started_hours FOR TESTING. + "! @testing {{p}}parking_eval + METHODS rates_per_tariff FOR TESTING. + "! @testing {{p}}parking_eval + METHODS fee_is_capped FOR TESTING. + "! @testing {{p}}parking_eval + METHODS invalid_tariff_ignored FOR TESTING. + "! @testing {{p}}parking_eval + METHODS zero_duration_ignored FOR TESTING. + "! @testing {{p}}parking_eval + METHODS groups_and_sorts_parks FOR TESTING. + "! @testing {{p}}parking_eval + METHODS sums_sessions_of_park FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}parking_eval( ). + ENDMETHOD. + + METHOD empty_input. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD free_minutes. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 20 ) + ( park_id = 'P001' tariff = 'A' duration_min = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = lt_result[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-session_cnt ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = ls_row-total_min ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_000 act = ls_row-total_fee ). + ENDMETHOD. + + METHOD one_started_hour. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 31 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_200 act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD two_started_hours. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 91 ) + ( park_id = 'P002' tariff = 'A' duration_min = 150 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_400 act = lt_result[ 1 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_400 act = lt_result[ 2 ]-total_fee ). + ENDMETHOD. + + METHOD rates_per_tariff. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 90 ) + ( park_id = 'P002' tariff = 'B' duration_min = 90 ) + ( park_id = 'P003' tariff = 'C' duration_min = 90 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_200 act = lt_result[ 1 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_350 act = lt_result[ 2 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_500 act = lt_result[ 3 ]-total_fee ). + ENDMETHOD. + + METHOD fee_is_capped. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'C' duration_min = 400 ) + ( park_id = 'P002' tariff = 'C' duration_min = 150 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_2000 act = lt_result[ 1 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_1000 act = lt_result[ 2 ]-total_fee ). + ENDMETHOD. + + METHOD invalid_tariff_ignored. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'X' duration_min = 120 ) + ( park_id = 'P002' tariff = 'A' duration_min = 90 ) + ( park_id = 'P002' tariff = 'X' duration_min = 200 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = lt_result[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'P002' act = ls_row-park_id ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-session_cnt ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_row-total_min ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_200 act = ls_row-total_fee ). + ENDMETHOD. + + METHOD zero_duration_ignored. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 0 ) + ( park_id = 'P001' tariff = 'B' duration_min = -5 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD groups_and_sorts_parks. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P003' tariff = 'A' duration_min = 60 ) + ( park_id = 'P001' tariff = 'B' duration_min = 60 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'P001' act = lt_result[ 1 ]-park_id ). + cl_abap_unit_assert=>assert_equals( exp = 'P003' act = lt_result[ 2 ]-park_id ). + ENDMETHOD. + + METHOD sums_sessions_of_park. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 45 ) + ( park_id = 'P001' tariff = 'B' duration_min = 200 ) + ( park_id = 'P001' tariff = 'A' duration_min = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = lt_result[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_row-session_cnt ). + cl_abap_unit_assert=>assert_equals( exp = 275 act = ls_row-total_min ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_1250 act = ls_row-total_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1073/reference/parking_eval.clas.abap b/tasks_gen/train/G1073/reference/parking_eval.clas.abap new file mode 100644 index 0000000..a28386f --- /dev/null +++ b/tasks_gen/train/G1073/reference/parking_eval.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}parking_eval DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_eval. + + PRIVATE SECTION. + TYPES: + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_total, + park_id TYPE {{p}}if_park_eval=>ty_park_id, + session_cnt TYPE i, + total_min TYPE i, + total_fee TYPE ty_amount, + END OF ty_total, + tt_total TYPE HASHED TABLE OF ty_total WITH UNIQUE KEY park_id. + + CONSTANTS: + BEGIN OF c_tariff, + a TYPE {{p}}if_park_eval=>ty_tariff VALUE 'A', + b TYPE {{p}}if_park_eval=>ty_tariff VALUE 'B', + c TYPE {{p}}if_park_eval=>ty_tariff VALUE 'C', + END OF c_tariff. + + CONSTANTS: + c_free_min TYPE i VALUE 30, + c_max_fee TYPE ty_amount VALUE '20.00', + c_rate_a TYPE ty_amount VALUE '2.00', + c_rate_b TYPE ty_amount VALUE '3.50', + c_rate_c TYPE ty_amount VALUE '5.00'. + + METHODS is_valid + IMPORTING is_session TYPE {{p}}if_park_eval=>ty_session + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS fee + IMPORTING is_session TYPE {{p}}if_park_eval=>ty_session + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS rate + IMPORTING iv_tariff TYPE {{p}}if_park_eval=>ty_tariff + RETURNING VALUE(rv_rate) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}parking_eval IMPLEMENTATION. + METHOD {{p}}if_park_eval~summarize. + DATA lt_total TYPE tt_total. + + LOOP AT it_session INTO DATA(ls_session). + IF is_valid( ls_session ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY park_id = ls_session-park_id + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT INITIAL LINE INTO TABLE lt_total ASSIGNING . + -park_id = ls_session-park_id. + ENDIF. + + -session_cnt += 1. + -total_min += ls_session-duration_min. + -total_fee += fee( ls_session ). + ENDLOOP. + + rt_summary = CORRESPONDING #( lt_total ). + SORT rt_summary BY park_id. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_session-duration_min > 0 AND rate( is_session-tariff ) > 0 ). + ENDMETHOD. + + METHOD fee. + IF is_session-duration_min <= c_free_min. + RETURN. + ENDIF. + + DATA(lv_hours) = ( is_session-duration_min - c_free_min + 59 ) DIV 60. + rv_fee = lv_hours * rate( is_session-tariff ). + + IF rv_fee > c_max_fee. + rv_fee = c_max_fee. + ENDIF. + ENDMETHOD. + + METHOD rate. + CASE iv_tariff. + WHEN c_tariff-a. + rv_rate = c_rate_a. + WHEN c_tariff-b. + rv_rate = c_rate_b. + WHEN c_tariff-c. + rv_rate = c_rate_c. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1073/reference/parking_eval.testclasses.abap b/tasks_gen/train/G1073/reference/parking_eval.testclasses.abap new file mode 100644 index 0000000..3316e59 --- /dev/null +++ b/tasks_gen/train/G1073/reference/parking_eval.testclasses.abap @@ -0,0 +1,75 @@ +CLASS ltc_parking_eval DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_fee_000 TYPE p LENGTH 8 DECIMALS 2 VALUE '0.00', + c_fee_200 TYPE p LENGTH 8 DECIMALS 2 VALUE '2.00', + c_fee_2000 TYPE p LENGTH 8 DECIMALS 2 VALUE '20.00'. + + DATA mo_cut TYPE REF TO {{p}}if_park_eval. + + METHODS setup. + "! @testing {{p}}parking_eval + METHODS free_minutes_are_free FOR TESTING. + "! @testing {{p}}parking_eval + METHODS one_hour_is_charged FOR TESTING. + "! @testing {{p}}parking_eval + METHODS fee_is_capped FOR TESTING. + "! @testing {{p}}parking_eval + METHODS invalid_tariff_is_ignored FOR TESTING. + "! @testing {{p}}parking_eval + METHODS groups_by_park FOR TESTING. + "! @testing {{p}}parking_eval + METHODS empty_input_gives_no_row FOR TESTING. +ENDCLASS. + + +CLASS ltc_parking_eval IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}parking_eval( ). + ENDMETHOD. + + METHOD free_minutes_are_free. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_000 act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD one_hour_is_charged. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'A' duration_min = 31 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_200 act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD fee_is_capped. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'C' duration_min = 400 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_2000 act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD invalid_tariff_is_ignored. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P001' tariff = 'X' duration_min = 120 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD groups_by_park. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( + ( park_id = 'P002' tariff = 'A' duration_min = 60 ) + ( park_id = 'P001' tariff = 'A' duration_min = 60 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'P001' act = lt_result[ 1 ]-park_id ). + cl_abap_unit_assert=>assert_equals( exp = 'P002' act = lt_result[ 2 ]-park_id ). + ENDMETHOD. + + METHOD empty_input_gives_no_row. + DATA(lt_result) = mo_cut->summarize( VALUE {{p}}if_park_eval=>tt_session( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1073/seed/if_park_eval.intf.abap b/tasks_gen/train/G1073/seed/if_park_eval.intf.abap new file mode 100644 index 0000000..4eea7de --- /dev/null +++ b/tasks_gen/train/G1073/seed/if_park_eval.intf.abap @@ -0,0 +1,22 @@ +INTERFACE {{p}}if_park_eval PUBLIC. + TYPES: + ty_park_id TYPE c LENGTH 4, + ty_tariff TYPE c LENGTH 1, + BEGIN OF ty_session, + park_id TYPE ty_park_id, + tariff TYPE ty_tariff, + duration_min TYPE i, + END OF ty_session, + tt_session TYPE STANDARD TABLE OF ty_session WITH EMPTY KEY, + BEGIN OF ty_summary, + park_id TYPE ty_park_id, + session_cnt TYPE i, + total_min TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_session TYPE tt_session + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDINTERFACE. diff --git a/tasks_gen/train/G1073/spec.md b/tasks_gen/train/G1073/spec.md new file mode 100644 index 0000000..2f30761 --- /dev/null +++ b/tasks_gen/train/G1073/spec.md @@ -0,0 +1,57 @@ +# 1. Goal +Summarise the parking sessions of the car parks of a car park operator. The +operations team calls the class for each day. The class returns one row for each +car park with the number of sessions, the total parking time and the total fee. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_PARK_EVAL exists in package $TMP. It is active. +- The interface has the type TY_PARK_ID (CHAR 4), the type TY_TARIFF (CHAR 1), + the structure TY_SESSION with the components PARK_ID, TARIFF and DURATION_MIN, + the table type TT_SESSION (a standard table of TY_SESSION with an empty key), + the structure TY_SUMMARY with the components PARK_ID, SESSION_CNT, TOTAL_MIN + and TOTAL_FEE, the table type TT_SUMMARY (a standard table of TY_SUMMARY with + an empty key), and one method: + SUMMARIZE IMPORTING it_session TYPE tt_session + RETURNING VALUE(rt_summary) TYPE tt_summary. +- DURATION_MIN is the parking time of one session in minutes. +- The operations program collects the sessions of one day and passes them to the + class. + +# 4. Contract +- Create the class {{P}}PARKING_EVAL in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class implements {{P}}IF_PARK_EVAL. +- Do not add other public methods. + +# 5. Business rules +1. A session is valid when its tariff is A, B or C and its duration is greater + than 0 minutes. Ignore an invalid session completely. Do not count it, do not + add its duration and do not add a fee. +2. The first 30 minutes of a valid session are free. +3. The operator charges each started hour after the first 30 minutes. The first + started hour covers the minutes 31 to 90, the second started hour covers the + minutes 91 to 150, and so on. +4. The price of one started hour depends on the tariff: + A = 2.00 EUR, B = 3.50 EUR, C = 5.00 EUR. +5. The fee of one session is at most 20.00 EUR. +6. The result has one row for each car park with at least one valid session. +7. SESSION_CNT is the number of valid sessions of the car park. +8. TOTAL_MIN is the sum of the durations of the valid sessions of the car park. +9. TOTAL_FEE is the sum of the fees of the valid sessions of the car park. +10. Sort the result in ascending order of PARK_ID. +11. An empty input gives an empty result. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- 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_PARK_EVAL. Do not create database tables, + CDS views or other objects. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1073/task.json b/tasks_gen/train/G1073/task.json new file mode 100644 index 0000000..022a01c --- /dev/null +++ b/tasks_gen/train/G1073/task.json @@ -0,0 +1,52 @@ +{ + "id": "G1073", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_PARK_EVAL", + "file": "seed/if_park_eval.intf.abap", + "description": "Car park session and summary contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_EVAL", + "implements": "{{P}}IF_PARK_EVAL" + } + ], + "out_of_scope": [ + "{{P}}IF_PARK_EVAL" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests for the car park summary" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PARKING_EVAL", + "file": "reference/parking_eval.clas.abap", + "description": "Car park session summary", + "testclasses_file": "reference/parking_eval.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1074/faulty/m0_pallet_allocator.clas.abap b/tasks_gen/train/G1074/faulty/m0_pallet_allocator.clas.abap new file mode 100644 index 0000000..2f6256d --- /dev/null +++ b/tasks_gen/train/G1074/faulty/m0_pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) <> abap_false + OR iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack > c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 + ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/faulty/m1_pallet_allocator.clas.abap b/tasks_gen/train/G1074/faulty/m1_pallet_allocator.clas.abap new file mode 100644 index 0000000..7aa988f --- /dev/null +++ b/tasks_gen/train/G1074/faulty/m1_pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) = abap_false + AND iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack > c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 + ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/faulty/m2_pallet_allocator.clas.abap b/tasks_gen/train/G1074/faulty/m2_pallet_allocator.clas.abap new file mode 100644 index 0000000..d1e1b50 --- /dev/null +++ b/tasks_gen/train/G1074/faulty/m2_pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) = abap_false + OR iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_true ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack > c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 + ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/faulty/m3_pallet_allocator.clas.abap b/tasks_gen/train/G1074/faulty/m3_pallet_allocator.clas.abap new file mode 100644 index 0000000..0602579 --- /dev/null +++ b/tasks_gen/train/G1074/faulty/m3_pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) = abap_false + OR iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack <= c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 + ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/faulty/m4_pallet_allocator.clas.abap b/tasks_gen/train/G1074/faulty/m4_pallet_allocator.clas.abap new file mode 100644 index 0000000..c91eca0 --- /dev/null +++ b/tasks_gen/train/G1074/faulty/m4_pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) = abap_false + OR iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack > c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 - ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/generation.json b/tasks_gen/train/G1074/generation.json new file mode 100644 index 0000000..9f0b413 --- /dev/null +++ b/tasks_gen/train/G1074/generation.json @@ -0,0 +1,31 @@ +{ + "id": "G1074", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/courier_pricer.clas.abap line 47: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/courier_pricer.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1050 (similarity spec 0.79, rules 0.52, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 2, + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1074/hidden/pallet_hidden.clas.abap b/tasks_gen/train/G1074/hidden/pallet_hidden.clas.abap new file mode 100644 index 0000000..1fd66ea --- /dev/null +++ b/tasks_gen/train/G1074/hidden/pallet_hidden.clas.abap @@ -0,0 +1,121 @@ +CLASS {{p}}pallet_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pallet_allocator. + + METHODS setup. + METHODS frozen_pallet_ok FOR TESTING. + METHODS chilled_pallet_ok FOR TESTING. + METHODS dry_pallet_ok FOR TESTING. + METHODS lower_case_and_blanks FOR TESTING. + METHODS rack_boundaries FOR TESTING. + METHODS height_too_high_invalid FOR TESTING. + METHODS height_too_low_invalid FOR TESTING. + METHODS weight_limits FOR TESTING. + METHODS unknown_category_invalid FOR TESTING. + METHODS blank_category_invalid FOR TESTING. +ENDCLASS. + + +CLASS {{p}}pallet_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}pallet_allocator( ). + ENDMETHOD. + + METHOD frozen_pallet_ok. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 100 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 'FZ' act = ls_slot-temp_zone ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_slot-rack ). + ENDMETHOD. + + METHOD chilled_pallet_ok. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'C' iv_weight_kg = 500 iv_height_cm = 200 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 'CZ' act = ls_slot-temp_zone ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_slot-rack ). + ENDMETHOD. + + METHOD dry_pallet_ok. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'D' iv_weight_kg = 20 iv_height_cm = 40 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'D4' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 'DZ' act = ls_slot-temp_zone ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_slot-rack ). + ENDMETHOD. + + METHOD lower_case_and_blanks. + DATA(ls_lower) = mo_cut->allocate( iv_category = 'f' iv_weight_kg = 50 iv_height_cm = 121 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_lower-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = ls_lower-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_lower-rack ). + + DATA(ls_blank) = mo_cut->allocate( iv_category = ' c' iv_weight_kg = 50 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_blank-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = ls_blank-aisle ). + ENDMETHOD. + + METHOD rack_boundaries. + DATA(ls_base) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 120 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_base-rack msg = `120` ). + + DATA(ls_step) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 121 ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_step-rack msg = `121` ). + + DATA(ls_max) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 240 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_max-is_valid msg = `240 valid` ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_max-rack msg = `240` ). + ENDMETHOD. + + METHOD height_too_high_invalid. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 241 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_initial( act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_initial( act = ls_slot-temp_zone ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_slot-rack ). + ENDMETHOD. + + METHOD height_too_low_invalid. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 39 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_slot-rack ). + ENDMETHOD. + + METHOD weight_limits. + DATA(ls_light) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 19 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_light-is_valid msg = `19` ). + + DATA(ls_min) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 20 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_min-is_valid msg = `20` ). + + DATA(ls_max) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 800 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_max-is_valid msg = `800` ). + + DATA(ls_heavy) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 801 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_heavy-is_valid msg = `801` ). + ENDMETHOD. + + METHOD unknown_category_invalid. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'X' iv_weight_kg = 100 iv_height_cm = 100 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_initial( act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_initial( act = ls_slot-temp_zone ). + ENDMETHOD. + + METHOD blank_category_invalid. + DATA(ls_slot) = mo_cut->allocate( iv_category = ' ' iv_weight_kg = 100 iv_height_cm = 100 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_slot-rack ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/mutation.json b/tasks_gen/train/G1074/mutation.json new file mode 100644 index 0000000..e8b4069 --- /dev/null +++ b/tasks_gen/train/G1074/mutation.json @@ -0,0 +1,72 @@ +{ + "task": "G1074", + "mutants": [ + { + "object": "{{P}}PALLET_ALLOCATOR", + "mutant": "line 68: = -> <> (eq)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "CHILLED_PALLET_OK", + "DRY_PALLET_OK", + "FROZEN_PALLET_OK", + "LOWER_CASE_AND_BLANKS", + "RACK_BOUNDARIES", + "WEIGHT_LIMITS" + ] + }, + { + "object": "{{P}}PALLET_ALLOCATOR", + "mutant": "line 69: OR -> AND (logic)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "HEIGHT_TOO_LOW_INVALID", + "WEIGHT_LIMITS" + ] + }, + { + "object": "{{P}}PALLET_ALLOCATOR", + "mutant": "line 70: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "BLANK_CATEGORY_INVALID", + "HEIGHT_TOO_LOW_INVALID", + "UNKNOWN_CATEGORY_INVALID", + "WEIGHT_LIMITS" + ] + }, + { + "object": "{{P}}PALLET_ALLOCATOR", + "mutant": "line 75: > -> <= (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "CHILLED_PALLET_OK", + "DRY_PALLET_OK", + "FROZEN_PALLET_OK", + "HEIGHT_TOO_HIGH_INVALID", + "LOWER_CASE_AND_BLANKS", + "RACK_BOUNDARIES", + "WEIGHT_LIMITS" + ] + }, + { + "object": "{{P}}PALLET_ALLOCATOR", + "mutant": "line 102: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "CHILLED_PALLET_OK", + "HEIGHT_TOO_HIGH_INVALID", + "LOWER_CASE_AND_BLANKS", + "RACK_BOUNDARIES" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1074/reference/pallet_allocator.clas.abap b/tasks_gen/train/G1074/reference/pallet_allocator.clas.abap new file mode 100644 index 0000000..bd2c2e4 --- /dev/null +++ b/tasks_gen/train/G1074/reference/pallet_allocator.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_zone, + category TYPE ty_category, + aisle TYPE c LENGTH 2, + temp TYPE c LENGTH 2, + END OF ty_zone, + tt_zone TYPE HASHED TABLE OF ty_zone WITH UNIQUE KEY category. + + CONSTANTS: + c_min_weight TYPE ty_weight VALUE 20, + c_max_weight TYPE ty_weight VALUE 800, + c_min_height TYPE ty_height VALUE 40, + c_base_height TYPE ty_height VALUE 120, + c_height_step TYPE ty_height VALUE 40, + c_max_rack TYPE i VALUE 4. + + METHODS zones + RETURNING VALUE(rt_zone) TYPE tt_zone. + METHODS normalized_category + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_category) TYPE ty_category. + METHODS rack_for_height + IMPORTING iv_height_cm TYPE ty_height + RETURNING VALUE(rv_rack) TYPE i. + METHODS is_weight_valid + IMPORTING iv_weight_kg TYPE ty_weight + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA(lv_category) = normalized_category( iv_category ). + DATA(lt_zone) = zones( ). + + IF NOT line_exists( lt_zone[ category = lv_category ] ) + OR is_weight_valid( iv_weight_kg ) = abap_false + OR iv_height_cm < c_min_height. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(lv_rack) = rack_for_height( iv_height_cm ). + IF lv_rack > c_max_rack. + rs_slot = VALUE #( is_valid = abap_false ). + RETURN. + ENDIF. + + DATA(ls_zone) = lt_zone[ category = lv_category ]. + rs_slot = VALUE #( aisle = ls_zone-aisle + rack = lv_rack + temp_zone = ls_zone-temp + is_valid = abap_true ). + ENDMETHOD. + + METHOD zones. + rt_zone = VALUE #( ( category = 'F' aisle = 'A1' temp = 'FZ' ) + ( category = 'C' aisle = 'B2' temp = 'CZ' ) + ( category = 'D' aisle = 'D4' temp = 'DZ' ) ). + ENDMETHOD. + + METHOD normalized_category. + rv_category = to_upper( replace( val = iv_category sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD rack_for_height. + IF iv_height_cm <= c_base_height. + rv_rack = 1. + RETURN. + ENDIF. + rv_rack = 1 + ( iv_height_cm - c_base_height + c_height_step - 1 ) DIV c_height_step. + ENDMETHOD. + + METHOD is_weight_valid. + rv_valid = xsdbool( iv_weight_kg >= c_min_weight AND iv_weight_kg <= c_max_weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/reference/pallet_allocator.testclasses.abap b/tasks_gen/train/G1074/reference/pallet_allocator.testclasses.abap new file mode 100644 index 0000000..e0f8f2d --- /dev/null +++ b/tasks_gen/train/G1074/reference/pallet_allocator.testclasses.abap @@ -0,0 +1,67 @@ +CLASS ltc_pallet_allocator DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pallet_allocator. + + METHODS setup. + METHODS frozen_pallet FOR TESTING. + METHODS chilled_pallet FOR TESTING. + METHODS dry_pallet FOR TESTING. + METHODS unknown_category FOR TESTING. + METHODS weight_out_of_range FOR TESTING. + METHODS height_out_of_range FOR TESTING. +ENDCLASS. + + +CLASS ltc_pallet_allocator IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}pallet_allocator( ). + ENDMETHOD. + + METHOD frozen_pallet. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 100 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_true act = ls_slot-is_valid ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_slot-rack ). + ENDMETHOD. + + METHOD chilled_pallet. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'C' iv_weight_kg = 500 iv_height_cm = 200 ). + + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 'CZ' act = ls_slot-temp_zone ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_slot-rack ). + ENDMETHOD. + + METHOD dry_pallet. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'D' iv_weight_kg = 20 iv_height_cm = 40 ). + + cl_abap_unit_assert=>assert_equals( exp = 'D4' act = ls_slot-aisle ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_slot-rack ). + ENDMETHOD. + + METHOD unknown_category. + DATA(ls_slot) = mo_cut->allocate( iv_category = 'X' iv_weight_kg = 100 iv_height_cm = 100 ). + + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_slot-is_valid ). + ENDMETHOD. + + METHOD weight_out_of_range. + DATA(ls_light) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 19 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_light-is_valid ). + + DATA(ls_heavy) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 801 iv_height_cm = 100 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_heavy-is_valid ). + ENDMETHOD. + + METHOD height_out_of_range. + DATA(ls_low) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 39 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_low-is_valid ). + + DATA(ls_high) = mo_cut->allocate( iv_category = 'F' iv_weight_kg = 100 iv_height_cm = 241 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = ls_high-is_valid ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/seed/pallet_allocator.clas.abap b/tasks_gen/train/G1074/seed/pallet_allocator.clas.abap new file mode 100644 index 0000000..2d0f5f5 --- /dev/null +++ b/tasks_gen/train/G1074/seed/pallet_allocator.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}pallet_allocator DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_category TYPE c LENGTH 2. + TYPES ty_weight TYPE i. + TYPES ty_height TYPE i. + TYPES: + BEGIN OF ty_slot, + aisle TYPE c LENGTH 2, + rack TYPE i, + temp_zone TYPE c LENGTH 2, + is_valid TYPE abap_bool, + END OF ty_slot. + + METHODS constructor. + METHODS allocate + IMPORTING + iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING + VALUE(rs_slot) TYPE ty_slot. +ENDCLASS. + + +CLASS {{p}}pallet_allocator IMPLEMENTATION. + + METHOD constructor. + ENDMETHOD. + + METHOD allocate. + DATA: lv_cat TYPE ty_category, + lv_aisle TYPE c LENGTH 2, + lv_zone TYPE c LENGTH 2, + lv_rack TYPE i, + lv_h TYPE i, + lv_ok TYPE c LENGTH 1. + + CLEAR rs_slot. + lv_cat = iv_category. + TRANSLATE lv_cat TO UPPER CASE. + CONDENSE lv_cat NO-GAPS. + + lv_ok = 'X'. + + IF lv_cat = 'F'. + lv_aisle = 'A1'. + lv_zone = 'FZ'. + ELSEIF lv_cat = 'C'. + lv_aisle = 'B2'. + lv_zone = 'CZ'. + ELSEIF lv_cat = 'D'. + lv_aisle = 'D4'. + lv_zone = 'DZ'. + ELSE. + lv_ok = ' '. + ENDIF. + + IF iv_weight_kg < 20. + lv_ok = ' '. + ENDIF. + + IF iv_weight_kg > 800. + lv_ok = ' '. + ENDIF. + + IF iv_height_cm < 40. + lv_ok = ' '. + ENDIF. + + IF lv_ok = 'X'. + lv_rack = 1. + lv_h = iv_height_cm. + WHILE lv_h > 120. + lv_rack = lv_rack + 1. + lv_h = lv_h - 40. + ENDWHILE. + IF lv_rack > 4. + lv_ok = ' '. + ENDIF. + ENDIF. + + IF lv_ok = 'X'. + rs_slot-aisle = lv_aisle. + rs_slot-rack = lv_rack. + rs_slot-temp_zone = lv_zone. + rs_slot-is_valid = abap_true. + ELSE. + CLEAR rs_slot. + rs_slot-is_valid = abap_false. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1074/spec.md b/tasks_gen/train/G1074/spec.md new file mode 100644 index 0000000..5b5f34e --- /dev/null +++ b/tasks_gen/train/G1074/spec.md @@ -0,0 +1,59 @@ +# 1. Goal +A cold-storage warehouse stores pallets of food. The gate system calls the class +{{P}}PALLET_ALLOCATOR in package $TMP for each incoming pallet. The class decides +the storage aisle, the rack, and the temperature zone of the pallet. + +The class is active. Its implementation is legacy code. The method ALLOCATE is +long, it uses many literal values and nested IF blocks, and it is hard to +maintain. Users report that the class is hard to test. + +Refactor the class in place. The class name and the public interface stay the +same. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}PALLET_ALLOCATOR exists in package $TMP. It is active. +- The class is public, final, and has a public constructor without parameters. +- The class has one public method, ALLOCATE. The legacy implementation of + ALLOCATE contains the storage rules of the warehouse. +- The gate system calls ALLOCATE for each pallet and prints the result on the + pallet label. The result must not change. + +# 4. Contract +- The class {{P}}PALLET_ALLOCATOR stays in package $TMP. The class name does not + change. +- The class stays public and final. The public constructor stays without + parameters. +- The public types do not change: + - TY_CATEGORY (CHAR 2) + - TY_WEIGHT (INT) + - TY_HEIGHT (INT) + - TY_SLOT with the components AISLE (CHAR 2), RACK (INT), TEMP_ZONE (CHAR 2) + and IS_VALID (ABAP_BOOL). +- The public method does not change: + ALLOCATE + IMPORTING iv_category TYPE ty_category + iv_weight_kg TYPE ty_weight + iv_height_cm TYPE ty_height + RETURNING VALUE(rs_slot) TYPE ty_slot. +- Do not add other public methods or public attributes. + +# 5. Business rules +The legacy implementation of ALLOCATE is the specification. Read the legacy +code. The class must return the same result for every input. Do not change the +behavior. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. Use constants and small helper methods. No comment that restates + the code. +- Out of scope: do not change the public interface of the class. + +# 7. Acceptance +- The class is active and has no syntax error. +- The behavior of the class is unchanged. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1074/task.json b/tasks_gen/train/G1074/task.json new file mode 100644 index 0000000..b22fbd1 --- /dev/null +++ b/tasks_gen/train/G1074/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1074", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}PALLET_ALLOCATOR", + "file": "seed/pallet_allocator.clas.abap", + "description": "Legacy cold-storage pallet allocation class with the storage rules" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PALLET_ALLOCATOR" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}PALLET_HIDDEN", + "file": "hidden/pallet_hidden.clas.abap", + "description": "Behavior tests for the refactored pallet allocator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PALLET_ALLOCATOR", + "file": "reference/pallet_allocator.clas.abap", + "description": "Refactored cold-storage pallet allocator", + "testclasses_file": "reference/pallet_allocator.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1075/faulty/m0_childcare_pricer.clas.abap b/tasks_gen/train/G1075/faulty/m0_childcare_pricer.clas.abap new file mode 100644 index 0000000..8b23514 --- /dev/null +++ b/tasks_gen/train/G1075/faulty/m0_childcare_pricer.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}childcare_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_age_band TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 6. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + 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', + age TYPE ty_reason VALUE 'AGE', + days TYPE ty_reason VALUE 'DAYS', + END OF c_reason. + + METHODS calculate + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + band TYPE ty_age_band, + full_day TYPE ty_amount, + half_day TYPE ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY band. + + CONSTANTS: + BEGIN OF c_band, + baby TYPE ty_age_band VALUE 'BABY', + todd TYPE ty_age_band VALUE 'TODD', + pre TYPE ty_age_band VALUE 'PRE', + END OF c_band. + + CONSTANTS: + c_weeks_per_month TYPE p LENGTH 4 DECIMALS 3 VALUE '4.348', + c_sibling_discount TYPE p LENGTH 4 DECIMALS 3 VALUE '0.900', + c_max_fee TYPE ty_amount VALUE '1050.00'. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS age_band + IMPORTING iv_age_months TYPE i + RETURNING VALUE(rv_band) TYPE ty_age_band. + METHODS daily_rate + IMPORTING iv_band TYPE ty_age_band + iv_full_day TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS monthly_fee + IMPORTING iv_rate TYPE ty_amount + iv_days TYPE i + iv_sibling TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}childcare_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_age_months < 3 OR iv_age_months > 60. + rs_result-reason = c_reason-age. + RETURN. + ENDIF. + IF iv_days >= 1 OR iv_days > 5. + rs_result-reason = c_reason-days. + RETURN. + ENDIF. + DATA(lv_rate) = daily_rate( iv_band = age_band( iv_age_months ) + iv_full_day = iv_full_day ). + rs_result-fee = monthly_fee( iv_rate = lv_rate + iv_days = iv_days + iv_sibling = iv_sibling ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( band = c_band-baby + full_day = '62.00' + half_day = '38.00' ) + ( band = c_band-todd + full_day = '54.00' + half_day = '33.00' ) + ( band = c_band-pre + full_day = '46.00' + half_day = '28.00' ) ). + ENDMETHOD. + + METHOD age_band. + IF iv_age_months < 12. + rv_band = c_band-baby. + ELSEIF iv_age_months < 36. + rv_band = c_band-todd. + ELSE. + rv_band = c_band-pre. + ENDIF. + ENDMETHOD. + + METHOD daily_rate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ band = iv_band ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + RETURN. + ENDIF. + IF iv_full_day = abap_true. + rv_rate = -full_day. + ELSE. + rv_rate = -half_day. + ENDIF. + ENDMETHOD. + + METHOD monthly_fee. + DATA lv_gross TYPE p LENGTH 12 DECIMALS 5. + DATA lv_fee TYPE ty_amount. + lv_gross = iv_rate * iv_days * c_weeks_per_month. + lv_fee = round( val = lv_gross dec = 2 ). + IF iv_sibling = abap_true. + lv_gross = lv_fee * c_sibling_discount. + lv_fee = round( val = lv_gross dec = 2 ). + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rv_fee = lv_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/faulty/m1_childcare_pricer.clas.abap b/tasks_gen/train/G1075/faulty/m1_childcare_pricer.clas.abap new file mode 100644 index 0000000..74633fd --- /dev/null +++ b/tasks_gen/train/G1075/faulty/m1_childcare_pricer.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}childcare_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_age_band TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 6. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + 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', + age TYPE ty_reason VALUE 'AGE', + days TYPE ty_reason VALUE 'DAYS', + END OF c_reason. + + METHODS calculate + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + band TYPE ty_age_band, + full_day TYPE ty_amount, + half_day TYPE ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY band. + + CONSTANTS: + BEGIN OF c_band, + baby TYPE ty_age_band VALUE 'BABY', + todd TYPE ty_age_band VALUE 'TODD', + pre TYPE ty_age_band VALUE 'PRE', + END OF c_band. + + CONSTANTS: + c_weeks_per_month TYPE p LENGTH 4 DECIMALS 3 VALUE '4.348', + c_sibling_discount TYPE p LENGTH 4 DECIMALS 3 VALUE '0.900', + c_max_fee TYPE ty_amount VALUE '1050.00'. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS age_band + IMPORTING iv_age_months TYPE i + RETURNING VALUE(rv_band) TYPE ty_age_band. + METHODS daily_rate + IMPORTING iv_band TYPE ty_age_band + iv_full_day TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS monthly_fee + IMPORTING iv_rate TYPE ty_amount + iv_days TYPE i + iv_sibling TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}childcare_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_age_months < 3 OR iv_age_months > 60. + rs_result-reason = c_reason-age. + RETURN. + ENDIF. + IF iv_days < 1 OR iv_days > 5. + rs_result-reason = c_reason-days. + RETURN. + ENDIF. + DATA(lv_rate) = daily_rate( iv_band = age_band( iv_age_months ) + iv_full_day = iv_full_day ). + rs_result-fee = monthly_fee( iv_rate = lv_rate + iv_days = iv_days + iv_sibling = iv_sibling ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( band = c_band-baby + full_day = '62.00' + half_day = '38.00' ) + ( band = c_band-todd + full_day = '54.00' + half_day = '33.00' ) + ( band = c_band-pre + full_day = '46.00' + half_day = '28.00' ) ). + ENDMETHOD. + + METHOD age_band. + IF iv_age_months < 12. + rv_band = c_band-baby. + ELSEIF iv_age_months < 36. + rv_band = c_band-todd. + ELSE. + rv_band = c_band-pre. + ENDIF. + ENDMETHOD. + + METHOD daily_rate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ band = iv_band ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + RETURN. + ENDIF. + IF iv_full_day = abap_false. + rv_rate = -full_day. + ELSE. + rv_rate = -half_day. + ENDIF. + ENDMETHOD. + + METHOD monthly_fee. + DATA lv_gross TYPE p LENGTH 12 DECIMALS 5. + DATA lv_fee TYPE ty_amount. + lv_gross = iv_rate * iv_days * c_weeks_per_month. + lv_fee = round( val = lv_gross dec = 2 ). + IF iv_sibling = abap_true. + lv_gross = lv_fee * c_sibling_discount. + lv_fee = round( val = lv_gross dec = 2 ). + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rv_fee = lv_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/faulty/m2_childcare_pricer.clas.abap b/tasks_gen/train/G1075/faulty/m2_childcare_pricer.clas.abap new file mode 100644 index 0000000..4dfbf04 --- /dev/null +++ b/tasks_gen/train/G1075/faulty/m2_childcare_pricer.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}childcare_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_age_band TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 6. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + 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', + age TYPE ty_reason VALUE 'AGE', + days TYPE ty_reason VALUE 'DAYS', + END OF c_reason. + + METHODS calculate + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + band TYPE ty_age_band, + full_day TYPE ty_amount, + half_day TYPE ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY band. + + CONSTANTS: + BEGIN OF c_band, + baby TYPE ty_age_band VALUE 'BABY', + todd TYPE ty_age_band VALUE 'TODD', + pre TYPE ty_age_band VALUE 'PRE', + END OF c_band. + + CONSTANTS: + c_weeks_per_month TYPE p LENGTH 4 DECIMALS 3 VALUE '4.348', + c_sibling_discount TYPE p LENGTH 4 DECIMALS 3 VALUE '0.900', + c_max_fee TYPE ty_amount VALUE '1050.00'. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS age_band + IMPORTING iv_age_months TYPE i + RETURNING VALUE(rv_band) TYPE ty_age_band. + METHODS daily_rate + IMPORTING iv_band TYPE ty_age_band + iv_full_day TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS monthly_fee + IMPORTING iv_rate TYPE ty_amount + iv_days TYPE i + iv_sibling TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}childcare_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_age_months < 3 OR iv_age_months > 60. + rs_result-reason = c_reason-age. + RETURN. + ENDIF. + IF iv_days < 1 OR iv_days > 5. + rs_result-reason = c_reason-days. + RETURN. + ENDIF. + DATA(lv_rate) = daily_rate( iv_band = age_band( iv_age_months ) + iv_full_day = iv_full_day ). + rs_result-fee = monthly_fee( iv_rate = lv_rate + iv_days = iv_days + iv_sibling = iv_sibling ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( band = c_band-baby + full_day = '62.00' + half_day = '38.00' ) + ( band = c_band-todd + full_day = '54.00' + half_day = '33.00' ) + ( band = c_band-pre + full_day = '46.00' + half_day = '28.00' ) ). + ENDMETHOD. + + METHOD age_band. + IF iv_age_months < 12. + rv_band = c_band-baby. + ELSEIF iv_age_months < 36. + rv_band = c_band-todd. + ELSE. + rv_band = c_band-pre. + ENDIF. + ENDMETHOD. + + METHOD daily_rate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ band = iv_band ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + RETURN. + ENDIF. + IF iv_full_day = abap_true. + rv_rate = -full_day. + ELSE. + rv_rate = -half_day. + ENDIF. + ENDMETHOD. + + METHOD monthly_fee. + DATA lv_gross TYPE p LENGTH 12 DECIMALS 5. + DATA lv_fee TYPE ty_amount. + lv_gross = iv_rate * iv_days / c_weeks_per_month. + lv_fee = round( val = lv_gross dec = 2 ). + IF iv_sibling = abap_true. + lv_gross = lv_fee * c_sibling_discount. + lv_fee = round( val = lv_gross dec = 2 ). + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rv_fee = lv_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/faulty/m3_childcare_pricer.clas.abap b/tasks_gen/train/G1075/faulty/m3_childcare_pricer.clas.abap new file mode 100644 index 0000000..24fc150 --- /dev/null +++ b/tasks_gen/train/G1075/faulty/m3_childcare_pricer.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}childcare_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_age_band TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 6. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + 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', + age TYPE ty_reason VALUE 'AGE', + days TYPE ty_reason VALUE 'DAYS', + END OF c_reason. + + METHODS calculate + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + band TYPE ty_age_band, + full_day TYPE ty_amount, + half_day TYPE ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY band. + + CONSTANTS: + BEGIN OF c_band, + baby TYPE ty_age_band VALUE 'BABY', + todd TYPE ty_age_band VALUE 'TODD', + pre TYPE ty_age_band VALUE 'PRE', + END OF c_band. + + CONSTANTS: + c_weeks_per_month TYPE p LENGTH 4 DECIMALS 3 VALUE '4.348', + c_sibling_discount TYPE p LENGTH 4 DECIMALS 3 VALUE '0.900', + c_max_fee TYPE ty_amount VALUE '1050.00'. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS age_band + IMPORTING iv_age_months TYPE i + RETURNING VALUE(rv_band) TYPE ty_age_band. + METHODS daily_rate + IMPORTING iv_band TYPE ty_age_band + iv_full_day TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS monthly_fee + IMPORTING iv_rate TYPE ty_amount + iv_days TYPE i + iv_sibling TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}childcare_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_age_months < 3 OR iv_age_months > 60. + rs_result-reason = c_reason-age. + RETURN. + ENDIF. + IF iv_days < 1 OR iv_days > 5. + rs_result-reason = c_reason-days. + RETURN. + ENDIF. + DATA(lv_rate) = daily_rate( iv_band = age_band( iv_age_months ) + iv_full_day = iv_full_day ). + rs_result-fee = monthly_fee( iv_rate = lv_rate + iv_days = iv_days + iv_sibling = iv_sibling ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( band = c_band-baby + full_day = '62.00' + half_day = '38.00' ) + ( band = c_band-todd + full_day = '54.00' + half_day = '33.00' ) + ( band = c_band-pre + full_day = '46.00' + half_day = '28.00' ) ). + ENDMETHOD. + + METHOD age_band. + IF iv_age_months < 12. + rv_band = c_band-baby. + ELSEIF iv_age_months < 36. + rv_band = c_band-todd. + ELSE. + rv_band = c_band-pre. + ENDIF. + ENDMETHOD. + + METHOD daily_rate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ band = iv_band ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + RETURN. + ENDIF. + IF iv_full_day = abap_true. + rv_rate = -full_day. + ELSE. + rv_rate = -half_day. + ENDIF. + ENDMETHOD. + + METHOD monthly_fee. + DATA lv_gross TYPE p LENGTH 12 DECIMALS 5. + DATA lv_fee TYPE ty_amount. + lv_gross = iv_rate * iv_days * c_weeks_per_month. + lv_fee = round( val = lv_gross dec = 2 ). + IF iv_sibling <> abap_true. + lv_gross = lv_fee * c_sibling_discount. + lv_fee = round( val = lv_gross dec = 2 ). + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rv_fee = lv_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/generation.json b/tasks_gen/train/G1075/generation.json new file mode 100644 index 0000000..9571fe7 --- /dev/null +++ b/tasks_gen/train/G1075/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1075", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1075/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1075/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..8373859 --- /dev/null +++ b/tasks_gen/train/G1075/hidden/t02_hidden.clas.abap @@ -0,0 +1,196 @@ +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}}childcare_pricer. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_expected TYPE {{p}}childcare_pricer=>ty_amount + iv_actual TYPE {{p}}childcare_pricer=>ty_amount. + + METHODS baby_full_day_fee FOR TESTING. + METHODS toddler_half_day_fee FOR TESTING. + METHODS preschool_full_fee FOR TESTING. + METHODS band_boundaries FOR TESTING. + METHODS rounding_up FOR TESTING. + METHODS rounding_down FOR TESTING. + METHODS sibling_discount FOR TESTING. + METHODS fee_is_capped FOR TESTING. + METHODS cap_after_discount FOR TESTING. + METHODS sibling_below_cap FOR TESTING. + METHODS age_out_of_range FOR TESTING. + METHODS days_out_of_range FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}childcare_pricer( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD baby_full_day_fee. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 3 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '808.73' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD toddler_half_day_fee. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 3 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '430.45' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD preschool_full_fee. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 48 + iv_days = 5 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '1000.04' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD band_boundaries. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 3 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '165.22' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 11 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '165.22' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 12 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '143.48' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 35 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '143.48' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 36 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '121.74' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 60 + iv_days = 1 + iv_full_day = abap_false + iv_sibling = abap_false ). + assert_fee( iv_expected = '121.74' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD rounding_up. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 48 + iv_days = 1 + iv_full_day = abap_true + iv_sibling = abap_false ). + assert_fee( iv_expected = '200.01' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD rounding_down. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 2 + iv_full_day = abap_true + iv_sibling = abap_false ). + assert_fee( iv_expected = '539.15' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD sibling_discount. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 5 + iv_full_day = abap_false + iv_sibling = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '645.68' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD fee_is_capped. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 5 + iv_full_day = abap_true + iv_sibling = abap_false ). + assert_fee( iv_expected = '1050.00' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD cap_after_discount. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 5 + iv_full_day = abap_true + iv_sibling = abap_true ). + assert_fee( iv_expected = '1050.00' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD sibling_below_cap. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 4 + iv_full_day = abap_true + iv_sibling = abap_true ). + assert_fee( iv_expected = '970.47' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD age_out_of_range. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 2 + iv_days = 3 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-age + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 61 + iv_days = 3 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-age + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD days_out_of_range. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 0 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-days + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 24 + iv_days = 6 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-days + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + + ls_result = mo_cut->calculate( iv_age_months = 2 + iv_days = 9 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-age + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/mutation.json b/tasks_gen/train/G1075/mutation.json new file mode 100644 index 0000000..7975b48 --- /dev/null +++ b/tasks_gen/train/G1075/mutation.json @@ -0,0 +1,87 @@ +{ + "task": "G1075", + "mutants": [ + { + "object": "{{P}}CHILDCARE_PRICER", + "mutant": "line 71: < -> >= (rel)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "BABY_FULL_DAY_FEE", + "BAND_BOUNDARIES", + "CAP_AFTER_DISCOUNT", + "DAYS_OUT_OF_RANGE", + "FEE_IS_CAPPED", + "PRESCHOOL_FULL_FEE", + "ROUNDING_DOWN", + "ROUNDING_UP", + "SIBLING_BELOW_CAP", + "SIBLING_DISCOUNT", + "TODDLER_HALF_DAY_FEE" + ] + }, + { + "object": "{{P}}CHILDCARE_PRICER", + "mutant": "line 111: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "BABY_FULL_DAY_FEE", + "BAND_BOUNDARIES", + "CAP_AFTER_DISCOUNT", + "FEE_IS_CAPPED", + "PRESCHOOL_FULL_FEE", + "ROUNDING_DOWN", + "ROUNDING_UP", + "SIBLING_BELOW_CAP", + "SIBLING_DISCOUNT", + "TODDLER_HALF_DAY_FEE" + ] + }, + { + "object": "{{P}}CHILDCARE_PRICER", + "mutant": "line 121: * -> / (arith)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "BABY_FULL_DAY_FEE", + "BAND_BOUNDARIES", + "CAP_AFTER_DISCOUNT", + "FEE_IS_CAPPED", + "PRESCHOOL_FULL_FEE", + "ROUNDING_DOWN", + "ROUNDING_UP", + "SIBLING_BELOW_CAP", + "SIBLING_DISCOUNT", + "TODDLER_HALF_DAY_FEE" + ] + }, + { + "object": "{{P}}CHILDCARE_PRICER", + "mutant": "line 123: = -> <> (eq)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "BABY_FULL_DAY_FEE", + "BAND_BOUNDARIES", + "PRESCHOOL_FULL_FEE", + "ROUNDING_DOWN", + "ROUNDING_UP", + "SIBLING_BELOW_CAP", + "SIBLING_DISCOUNT", + "TODDLER_HALF_DAY_FEE" + ] + }, + { + "object": "{{P}}CHILDCARE_PRICER", + "mutant": "line 125: 2 -> 3 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1075/reference/childcare_pricer.clas.abap b/tasks_gen/train/G1075/reference/childcare_pricer.clas.abap new file mode 100644 index 0000000..223aa21 --- /dev/null +++ b/tasks_gen/train/G1075/reference/childcare_pricer.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}childcare_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_age_band TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 6. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + 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', + age TYPE ty_reason VALUE 'AGE', + days TYPE ty_reason VALUE 'DAYS', + END OF c_reason. + + METHODS calculate + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + band TYPE ty_age_band, + full_day TYPE ty_amount, + half_day TYPE ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY band. + + CONSTANTS: + BEGIN OF c_band, + baby TYPE ty_age_band VALUE 'BABY', + todd TYPE ty_age_band VALUE 'TODD', + pre TYPE ty_age_band VALUE 'PRE', + END OF c_band. + + CONSTANTS: + c_weeks_per_month TYPE p LENGTH 4 DECIMALS 3 VALUE '4.348', + c_sibling_discount TYPE p LENGTH 4 DECIMALS 3 VALUE '0.900', + c_max_fee TYPE ty_amount VALUE '1050.00'. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS age_band + IMPORTING iv_age_months TYPE i + RETURNING VALUE(rv_band) TYPE ty_age_band. + METHODS daily_rate + IMPORTING iv_band TYPE ty_age_band + iv_full_day TYPE abap_bool + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS monthly_fee + IMPORTING iv_rate TYPE ty_amount + iv_days TYPE i + iv_sibling TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}childcare_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_age_months < 3 OR iv_age_months > 60. + rs_result-reason = c_reason-age. + RETURN. + ENDIF. + IF iv_days < 1 OR iv_days > 5. + rs_result-reason = c_reason-days. + RETURN. + ENDIF. + DATA(lv_rate) = daily_rate( iv_band = age_band( iv_age_months ) + iv_full_day = iv_full_day ). + rs_result-fee = monthly_fee( iv_rate = lv_rate + iv_days = iv_days + iv_sibling = iv_sibling ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( band = c_band-baby + full_day = '62.00' + half_day = '38.00' ) + ( band = c_band-todd + full_day = '54.00' + half_day = '33.00' ) + ( band = c_band-pre + full_day = '46.00' + half_day = '28.00' ) ). + ENDMETHOD. + + METHOD age_band. + IF iv_age_months < 12. + rv_band = c_band-baby. + ELSEIF iv_age_months < 36. + rv_band = c_band-todd. + ELSE. + rv_band = c_band-pre. + ENDIF. + ENDMETHOD. + + METHOD daily_rate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ band = iv_band ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + RETURN. + ENDIF. + IF iv_full_day = abap_true. + rv_rate = -full_day. + ELSE. + rv_rate = -half_day. + ENDIF. + ENDMETHOD. + + METHOD monthly_fee. + DATA lv_gross TYPE p LENGTH 12 DECIMALS 5. + DATA lv_fee TYPE ty_amount. + lv_gross = iv_rate * iv_days * c_weeks_per_month. + lv_fee = round( val = lv_gross dec = 2 ). + IF iv_sibling = abap_true. + lv_gross = lv_fee * c_sibling_discount. + lv_fee = round( val = lv_gross dec = 2 ). + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rv_fee = lv_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/reference/childcare_pricer.testclasses.abap b/tasks_gen/train/G1075/reference/childcare_pricer.testclasses.abap new file mode 100644 index 0000000..4a7545e --- /dev/null +++ b/tasks_gen/train/G1075/reference/childcare_pricer.testclasses.abap @@ -0,0 +1,85 @@ +CLASS ltc_childcare_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}childcare_pricer. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_expected TYPE {{p}}childcare_pricer=>ty_amount + iv_actual TYPE {{p}}childcare_pricer=>ty_amount. + + METHODS baby_full_day FOR TESTING. + METHODS toddler_half_day FOR TESTING. + METHODS sibling_discount FOR TESTING. + METHODS fee_is_capped FOR TESTING. + METHODS age_out_of_range FOR TESTING. + METHODS days_out_of_range FOR TESTING. +ENDCLASS. + + +CLASS ltc_childcare_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}childcare_pricer( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD baby_full_day. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 3 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '808.73' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD toddler_half_day. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 3 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-ok + act = ls_result-reason ). + assert_fee( iv_expected = '430.45' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD sibling_discount. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 5 + iv_full_day = abap_false + iv_sibling = abap_true ). + assert_fee( iv_expected = '645.68' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD fee_is_capped. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 6 + iv_days = 5 + iv_full_day = abap_true + iv_sibling = abap_false ). + assert_fee( iv_expected = '1050.00' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD age_out_of_range. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 2 + iv_days = 3 + iv_full_day = abap_true + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-age + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + ENDMETHOD. + + METHOD days_out_of_range. + DATA(ls_result) = mo_cut->calculate( iv_age_months = 24 + iv_days = 6 + iv_full_day = abap_false + iv_sibling = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}childcare_pricer=>c_reason-days + act = ls_result-reason ). + assert_fee( iv_expected = '0.00' iv_actual = ls_result-fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1075/spec.md b/tasks_gen/train/G1075/spec.md new file mode 100644 index 0000000..dc7c01d --- /dev/null +++ b/tasks_gen/train/G1075/spec.md @@ -0,0 +1,65 @@ +# 1. Goal +Calculate the monthly fee for a child at a day care centre. The billing program +calls the calculation for each child before it creates the invoice. The program +needs the fee and one reason code for each child. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}CHILDCARE_PRICER does not exist yet. Create it in package $TMP. +- The billing program is not part of this task. It calls the calculation for + each child and reads the reason and the fee. + +# 4. Contract +- Create the class {{P}}CHILDCARE_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Define these public types: + TY_AGE_BAND TYPE c LENGTH 4 + TY_REASON TYPE c LENGTH 6 + TY_AMOUNT TYPE p LENGTH 8 DECIMALS 2 + TY_RESULT a structure with the components REASON (TY_REASON) and + FEE (TY_AMOUNT) +- Define the public constant structure C_REASON with the components OK, AGE and + DAYS. Each component has the type TY_REASON. +- Add one public method: + CALCULATE + IMPORTING iv_age_months TYPE i + iv_days TYPE i + iv_full_day TYPE abap_bool + iv_sibling TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. If iv_age_months is less than 3 or more than 60, return the reason AGE and + the fee 0. +2. If iv_days is less than 1 or more than 5, return the reason DAYS and the fee + 0. +3. Find the age band of the child: + 3 to 11 months: BABY + 12 to 35 months: TODD + 36 to 60 months: PRE +4. Find the daily rate of the age band: + BABY: 62.00 for a full day, 38.00 for a half day + TODD: 54.00 for a full day, 33.00 for a half day + PRE: 46.00 for a full day, 28.00 for a half day + iv_full_day = abap_true means a full day. All other values mean a half day. +5. The monthly fee is the daily rate multiplied by iv_days and by 4.348. Round + the result to 2 decimal places. +6. If iv_sibling = abap_true, multiply the monthly fee by 0.9 and round the + result to 2 decimal places. +7. If the monthly fee is more than 1050.00, set it to 1050.00. +8. Return the reason OK and the monthly fee. + +# 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: no other object. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1075/task.json b/tasks_gen/train/G1075/task.json new file mode 100644 index 0000000..f8d2b61 --- /dev/null +++ b/tasks_gen/train/G1075/task.json @@ -0,0 +1,41 @@ +{ + "id": "G1075", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CHILDCARE_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the childcare monthly fee" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CHILDCARE_PRICER", + "file": "reference/childcare_pricer.clas.abap", + "description": "Childcare monthly fee calculation", + "testclasses_file": "reference/childcare_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1076/faulty/m0_claim_checker.clas.abap b/tasks_gen/train/G1076/faulty/m0_claim_checker.clas.abap new file mode 100644 index 0000000..3444d1f --- /dev/null +++ b/tasks_gen/train/G1076/faulty/m0_claim_checker.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}claim_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_policy_id TYPE c LENGTH 10, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_claim, + policy_id TYPE ty_policy_id, + vehicle_age TYPE i, + damage TYPE ty_amount, + deductible TYPE ty_amount, + report_days TYPE i, + photo_count TYPE i, + END OF ty_claim. + + CONSTANTS c_max_payout TYPE ty_amount VALUE '10000.00'. + + METHODS check_claim + IMPORTING is_claim TYPE ty_claim + RETURNING VALUE(rv_payout) TYPE ty_amount + RAISING {{p}}cx_claim_error. + + PRIVATE SECTION. + METHODS validate + IMPORTING is_claim TYPE ty_claim + RAISING {{p}}cx_claim_error. + + METHODS raise_error + IMPORTING iv_reason TYPE {{p}}cx_claim_error=>ty_reason + RAISING {{p}}cx_claim_error. +ENDCLASS. + + +CLASS {{p}}claim_checker IMPLEMENTATION. + METHOD check_claim. + validate( is_claim ). + rv_payout = is_claim-damage + is_claim-deductible. + IF rv_payout > c_max_payout. + rv_payout = c_max_payout. + ENDIF. + ENDMETHOD. + + METHOD validate. + IF substring( val = is_claim-policy_id len = 1 ) <> 'P' + OR substring( val = is_claim-policy_id off = 1 ) CN '0123456789'. + raise_error( 'POLICY' ). + ENDIF. + + IF is_claim-report_days < 0 OR is_claim-report_days > 14. + raise_error( 'LATE' ). + ENDIF. + + IF is_claim-vehicle_age < 0 OR is_claim-vehicle_age > 12. + raise_error( 'AGED' ). + ENDIF. + + IF is_claim-photo_count < 2. + raise_error( 'PHOTOS' ). + ENDIF. + + IF is_claim-damage <= 0. + raise_error( 'DAMAGE' ). + ENDIF. + + IF is_claim-deductible < 0 OR is_claim-deductible > is_claim-damage. + raise_error( 'DEDUCT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + RAISE EXCEPTION TYPE {{p}}cx_claim_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/faulty/m3_claim_checker.clas.abap b/tasks_gen/train/G1076/faulty/m3_claim_checker.clas.abap new file mode 100644 index 0000000..ac6f140 --- /dev/null +++ b/tasks_gen/train/G1076/faulty/m3_claim_checker.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}claim_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_policy_id TYPE c LENGTH 10, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_claim, + policy_id TYPE ty_policy_id, + vehicle_age TYPE i, + damage TYPE ty_amount, + deductible TYPE ty_amount, + report_days TYPE i, + photo_count TYPE i, + END OF ty_claim. + + CONSTANTS c_max_payout TYPE ty_amount VALUE '10000.00'. + + METHODS check_claim + IMPORTING is_claim TYPE ty_claim + RETURNING VALUE(rv_payout) TYPE ty_amount + RAISING {{p}}cx_claim_error. + + PRIVATE SECTION. + METHODS validate + IMPORTING is_claim TYPE ty_claim + RAISING {{p}}cx_claim_error. + + METHODS raise_error + IMPORTING iv_reason TYPE {{p}}cx_claim_error=>ty_reason + RAISING {{p}}cx_claim_error. +ENDCLASS. + + +CLASS {{p}}claim_checker IMPLEMENTATION. + METHOD check_claim. + validate( is_claim ). + rv_payout = is_claim-damage - is_claim-deductible. + IF rv_payout > c_max_payout. + rv_payout = c_max_payout. + ENDIF. + ENDMETHOD. + + METHOD validate. + IF substring( val = is_claim-policy_id len = 1 ) <> 'P' + OR substring( val = is_claim-policy_id off = 1 ) CN '0123456789'. + raise_error( 'POLICY' ). + ENDIF. + + IF is_claim-report_days < 0 AND is_claim-report_days > 14. + raise_error( 'LATE' ). + ENDIF. + + IF is_claim-vehicle_age < 0 OR is_claim-vehicle_age > 12. + raise_error( 'AGED' ). + ENDIF. + + IF is_claim-photo_count < 2. + raise_error( 'PHOTOS' ). + ENDIF. + + IF is_claim-damage <= 0. + raise_error( 'DAMAGE' ). + ENDIF. + + IF is_claim-deductible < 0 OR is_claim-deductible > is_claim-damage. + raise_error( 'DEDUCT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + RAISE EXCEPTION TYPE {{p}}cx_claim_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/faulty/m4_claim_checker.clas.abap b/tasks_gen/train/G1076/faulty/m4_claim_checker.clas.abap new file mode 100644 index 0000000..e6f3152 --- /dev/null +++ b/tasks_gen/train/G1076/faulty/m4_claim_checker.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}claim_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_policy_id TYPE c LENGTH 10, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_claim, + policy_id TYPE ty_policy_id, + vehicle_age TYPE i, + damage TYPE ty_amount, + deductible TYPE ty_amount, + report_days TYPE i, + photo_count TYPE i, + END OF ty_claim. + + CONSTANTS c_max_payout TYPE ty_amount VALUE '10000.00'. + + METHODS check_claim + IMPORTING is_claim TYPE ty_claim + RETURNING VALUE(rv_payout) TYPE ty_amount + RAISING {{p}}cx_claim_error. + + PRIVATE SECTION. + METHODS validate + IMPORTING is_claim TYPE ty_claim + RAISING {{p}}cx_claim_error. + + METHODS raise_error + IMPORTING iv_reason TYPE {{p}}cx_claim_error=>ty_reason + RAISING {{p}}cx_claim_error. +ENDCLASS. + + +CLASS {{p}}claim_checker IMPLEMENTATION. + METHOD check_claim. + validate( is_claim ). + rv_payout = is_claim-damage - is_claim-deductible. + IF rv_payout > c_max_payout. + rv_payout = c_max_payout. + ENDIF. + ENDMETHOD. + + METHOD validate. + IF substring( val = is_claim-policy_id len = 1 ) <> 'P' + OR substring( val = is_claim-policy_id off = 1 ) CN '0123456789'. + raise_error( 'POLICY' ). + ENDIF. + + IF is_claim-report_days < 0 OR is_claim-report_days > 14. + raise_error( 'LATE' ). + ENDIF. + + IF is_claim-vehicle_age < 0 OR is_claim-vehicle_age > 12. + raise_error( 'AGED' ). + ENDIF. + + IF is_claim-photo_count >= 2. + raise_error( 'PHOTOS' ). + ENDIF. + + IF is_claim-damage <= 0. + raise_error( 'DAMAGE' ). + ENDIF. + + IF is_claim-deductible < 0 OR is_claim-deductible > is_claim-damage. + raise_error( 'DEDUCT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + RAISE EXCEPTION TYPE {{p}}cx_claim_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/generation.json b/tasks_gen/train/G1076/generation.json new file mode 100644 index 0000000..8c1ef61 --- /dev/null +++ b/tasks_gen/train/G1076/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1076", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1076/hidden/claim_check_hidden.clas.abap b/tasks_gen/train/G1076/hidden/claim_check_hidden.clas.abap new file mode 100644 index 0000000..c45aee9 --- /dev/null +++ b/tasks_gen/train/G1076/hidden/claim_check_hidden.clas.abap @@ -0,0 +1,158 @@ +CLASS {{p}}claim_check_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}claim_checker. + + METHODS setup. + METHODS valid_claim + RETURNING VALUE(rs_claim) TYPE {{p}}claim_checker=>ty_claim. + METHODS payout + IMPORTING is_claim TYPE {{p}}claim_checker=>ty_claim + RETURNING VALUE(rv_payout) TYPE {{p}}claim_checker=>ty_amount. + METHODS assert_reason + IMPORTING is_claim TYPE {{p}}claim_checker=>ty_claim + iv_reason TYPE {{p}}cx_claim_error=>ty_reason. + + METHODS rejects_wrong_prefix FOR TESTING. + METHODS rejects_blank_policy FOR TESTING. + METHODS rejects_late_report FOR TESTING. + METHODS rejects_negative_days FOR TESTING. + METHODS rejects_old_vehicle FOR TESTING. + METHODS accepts_max_age FOR TESTING. + METHODS rejects_few_photos FOR TESTING. + METHODS rejects_zero_damage FOR TESTING. + METHODS rejects_high_deductible FOR TESTING. + METHODS pays_damage_minus_deduct FOR TESTING. + METHODS caps_high_payout FOR TESTING. + METHODS policy_before_late FOR TESTING. +ENDCLASS. + + +CLASS {{p}}claim_check_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}claim_checker( ). + ENDMETHOD. + + METHOD valid_claim. + rs_claim = VALUE #( policy_id = 'P123456789' + vehicle_age = 3 + damage = '2500.00' + deductible = '500.00' + report_days = 5 + photo_count = 3 ). + ENDMETHOD. + + METHOD payout. + TRY. + rv_payout = mo_cut->check_claim( is_claim ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( + msg = |Unexpected rejection { lo_error->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_reason. + TRY. + mo_cut->check_claim( is_claim ). + cl_abap_unit_assert=>fail( + msg = |No exception with reason { iv_reason }| ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = iv_reason + act = lo_error->mv_reason + msg = 'reason' ). + cl_abap_unit_assert=>assert_char_cp( act = lo_error->get_text( ) + exp = |*{ iv_reason }*| + msg = 'message text' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_wrong_prefix. + DATA(ls_claim) = valid_claim( ). + ls_claim-policy_id = 'X123456789'. + assert_reason( is_claim = ls_claim + iv_reason = 'POLICY' ). + ENDMETHOD. + + METHOD rejects_blank_policy. + DATA(ls_claim) = valid_claim( ). + ls_claim-policy_id = 'P12 456789'. + assert_reason( is_claim = ls_claim + iv_reason = 'POLICY' ). + ENDMETHOD. + + METHOD rejects_late_report. + DATA(ls_claim) = valid_claim( ). + ls_claim-report_days = 15. + assert_reason( is_claim = ls_claim + iv_reason = 'LATE' ). + ENDMETHOD. + + METHOD rejects_negative_days. + DATA(ls_claim) = valid_claim( ). + ls_claim-report_days = -1. + assert_reason( is_claim = ls_claim + iv_reason = 'LATE' ). + ENDMETHOD. + + METHOD rejects_old_vehicle. + DATA(ls_claim) = valid_claim( ). + ls_claim-vehicle_age = 13. + assert_reason( is_claim = ls_claim + iv_reason = 'AGED' ). + ENDMETHOD. + + METHOD accepts_max_age. + DATA(ls_claim) = valid_claim( ). + ls_claim-vehicle_age = 12. + ls_claim-photo_count = 2. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}claim_checker=>ty_amount( '2000.00' ) + act = payout( ls_claim ) ). + ENDMETHOD. + + METHOD rejects_few_photos. + DATA(ls_claim) = valid_claim( ). + ls_claim-photo_count = 1. + assert_reason( is_claim = ls_claim + iv_reason = 'PHOTOS' ). + ENDMETHOD. + + METHOD rejects_zero_damage. + DATA(ls_claim) = valid_claim( ). + ls_claim-damage = 0. + assert_reason( is_claim = ls_claim + iv_reason = 'DAMAGE' ). + ENDMETHOD. + + METHOD rejects_high_deductible. + DATA(ls_claim) = valid_claim( ). + ls_claim-deductible = '3000.00'. + assert_reason( is_claim = ls_claim + iv_reason = 'DEDUCT' ). + ENDMETHOD. + + METHOD pays_damage_minus_deduct. + DATA(ls_claim) = valid_claim( ). + ls_claim-report_days = 14. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}claim_checker=>ty_amount( '2000.00' ) + act = payout( ls_claim ) ). + ENDMETHOD. + + METHOD caps_high_payout. + DATA(ls_claim) = valid_claim( ). + ls_claim-damage = '20000.00'. + ls_claim-deductible = '1000.00'. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}claim_checker=>c_max_payout + act = payout( ls_claim ) ). + ENDMETHOD. + + METHOD policy_before_late. + DATA(ls_claim) = valid_claim( ). + ls_claim-policy_id = 'X123456789'. + ls_claim-report_days = 30. + assert_reason( is_claim = ls_claim + iv_reason = 'POLICY' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/mutation.json b/tasks_gen/train/G1076/mutation.json new file mode 100644 index 0000000..668ecd9 --- /dev/null +++ b/tasks_gen/train/G1076/mutation.json @@ -0,0 +1,57 @@ +{ + "task": "G1076", + "mutants": [ + { + "object": "{{P}}CLAIM_CHECKER", + "mutant": "line 36: - -> + (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "ACCEPTS_MAX_AGE", + "PAYS_DAMAGE_MINUS_DEDUCT" + ] + }, + { + "object": "{{P}}CLAIM_CHECKER", + "mutant": "line 43: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}CLAIM_CHECKER", + "mutant": "line 44: 1 -> 2 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + }, + { + "object": "{{P}}CLAIM_CHECKER", + "mutant": "line 48: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "REJECTS_LATE_REPORT", + "REJECTS_NEGATIVE_DAYS" + ] + }, + { + "object": "{{P}}CLAIM_CHECKER", + "mutant": "line 56: < -> >= (rel)", + "status": "killed", + "hidden": "6/12", + "failed_tests": [ + "ACCEPTS_MAX_AGE", + "CAPS_HIGH_PAYOUT", + "PAYS_DAMAGE_MINUS_DEDUCT", + "REJECTS_FEW_PHOTOS", + "REJECTS_HIGH_DEDUCTIBLE", + "REJECTS_ZERO_DAMAGE" + ] + } + ], + "valid": 4, + "killed": 3, + "kill_rate": 0.75, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1076/reference/claim_checker.clas.abap b/tasks_gen/train/G1076/reference/claim_checker.clas.abap new file mode 100644 index 0000000..1f5a4c7 --- /dev/null +++ b/tasks_gen/train/G1076/reference/claim_checker.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}claim_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_policy_id TYPE c LENGTH 10, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_claim, + policy_id TYPE ty_policy_id, + vehicle_age TYPE i, + damage TYPE ty_amount, + deductible TYPE ty_amount, + report_days TYPE i, + photo_count TYPE i, + END OF ty_claim. + + CONSTANTS c_max_payout TYPE ty_amount VALUE '10000.00'. + + METHODS check_claim + IMPORTING is_claim TYPE ty_claim + RETURNING VALUE(rv_payout) TYPE ty_amount + RAISING {{p}}cx_claim_error. + + PRIVATE SECTION. + METHODS validate + IMPORTING is_claim TYPE ty_claim + RAISING {{p}}cx_claim_error. + + METHODS raise_error + IMPORTING iv_reason TYPE {{p}}cx_claim_error=>ty_reason + RAISING {{p}}cx_claim_error. +ENDCLASS. + + +CLASS {{p}}claim_checker IMPLEMENTATION. + METHOD check_claim. + validate( is_claim ). + rv_payout = is_claim-damage - is_claim-deductible. + IF rv_payout > c_max_payout. + rv_payout = c_max_payout. + ENDIF. + ENDMETHOD. + + METHOD validate. + IF substring( val = is_claim-policy_id len = 1 ) <> 'P' + OR substring( val = is_claim-policy_id off = 1 ) CN '0123456789'. + raise_error( 'POLICY' ). + ENDIF. + + IF is_claim-report_days < 0 OR is_claim-report_days > 14. + raise_error( 'LATE' ). + ENDIF. + + IF is_claim-vehicle_age < 0 OR is_claim-vehicle_age > 12. + raise_error( 'AGED' ). + ENDIF. + + IF is_claim-photo_count < 2. + raise_error( 'PHOTOS' ). + ENDIF. + + IF is_claim-damage <= 0. + raise_error( 'DAMAGE' ). + ENDIF. + + IF is_claim-deductible < 0 OR is_claim-deductible > is_claim-damage. + raise_error( 'DEDUCT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + RAISE EXCEPTION TYPE {{p}}cx_claim_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/reference/claim_checker.testclasses.abap b/tasks_gen/train/G1076/reference/claim_checker.testclasses.abap new file mode 100644 index 0000000..8cdb86e --- /dev/null +++ b/tasks_gen/train/G1076/reference/claim_checker.testclasses.abap @@ -0,0 +1,132 @@ +CLASS ltc_claim_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}claim_checker. + + METHODS setup. + METHODS claim + RETURNING VALUE(rs_claim) TYPE {{p}}claim_checker=>ty_claim. + METHODS payout + IMPORTING is_claim TYPE {{p}}claim_checker=>ty_claim + RETURNING VALUE(rv_payout) TYPE {{p}}claim_checker=>ty_amount. + + METHODS pays_claim FOR TESTING. + METHODS caps_payout FOR TESTING. + METHODS rejects_policy FOR TESTING. + METHODS rejects_late FOR TESTING. + METHODS rejects_old_car FOR TESTING. + METHODS rejects_photos FOR TESTING. + METHODS rejects_damage FOR TESTING. + METHODS rejects_deductible FOR TESTING. +ENDCLASS. + + +CLASS ltc_claim_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}claim_checker( ). + ENDMETHOD. + + METHOD claim. + rs_claim = VALUE #( policy_id = 'P123456789' + vehicle_age = 3 + damage = '2500.00' + deductible = '500.00' + report_days = 5 + photo_count = 3 ). + ENDMETHOD. + + METHOD payout. + TRY. + rv_payout = mo_cut->check_claim( is_claim ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( + msg = |Unexpected rejection { lo_error->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD pays_claim. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}claim_checker=>ty_amount( '2000.00' ) + act = payout( claim( ) ) ). + ENDMETHOD. + + METHOD caps_payout. + DATA(ls_claim) = claim( ). + ls_claim-damage = '50000.00'. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}claim_checker=>c_max_payout + act = payout( ls_claim ) ). + ENDMETHOD. + + METHOD rejects_policy. + DATA(ls_claim) = claim( ). + ls_claim-policy_id = 'X123456789'. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'POLICY' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_late. + DATA(ls_claim) = claim( ). + ls_claim-report_days = 20. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'LATE' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_old_car. + DATA(ls_claim) = claim( ). + ls_claim-vehicle_age = 20. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'AGED' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_photos. + DATA(ls_claim) = claim( ). + ls_claim-photo_count = 0. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'PHOTOS' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_damage. + DATA(ls_claim) = claim( ). + ls_claim-damage = 0. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'DAMAGE' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_deductible. + DATA(ls_claim) = claim( ). + ls_claim-deductible = '9000.00'. + TRY. + mo_cut->check_claim( ls_claim ). + cl_abap_unit_assert=>fail( msg = 'no exception' ). + CATCH {{p}}cx_claim_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'DEDUCT' + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/seed/cx_claim_error.clas.abap b/tasks_gen/train/G1076/seed/cx_claim_error.clas.abap new file mode 100644 index 0000000..5f0c0d7 --- /dev/null +++ b/tasks_gen/train/G1076/seed/cx_claim_error.clas.abap @@ -0,0 +1,24 @@ +CLASS {{p}}cx_claim_error DEFINITION PUBLIC FINAL CREATE PUBLIC + INHERITING FROM cx_static_check. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 12. + DATA mv_reason TYPE ty_reason READ-ONLY. + METHODS constructor + IMPORTING iv_reason TYPE ty_reason. + METHODS get_text REDEFINITION. + + PRIVATE SECTION. + CONSTANTS c_prefix TYPE string VALUE 'Claim rejected:'. +ENDCLASS. + + +CLASS {{p}}cx_claim_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. + + METHOD get_text. + result = |{ c_prefix } { mv_reason }|. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1076/spec.md b/tasks_gen/train/G1076/spec.md new file mode 100644 index 0000000..1c2c17e --- /dev/null +++ b/tasks_gen/train/G1076/spec.md @@ -0,0 +1,58 @@ +# 1. Goal +Check a car damage claim of an insurance company before the company pays it. +The claims program calls the check for each claim. The check returns the payout +amount or it stops with a reason. + +# 2. Open questions +None. + +# 3. Context +- The exception class {{P}}CX_CLAIM_ERROR exists in package $TMP. It is active. +- The class inherits from CX_STATIC_CHECK. +- The class has the public type TY_REASON (CHAR 12) and the read-only attribute + MV_REASON of type TY_REASON. +- The constructor of the class has the importing parameter IV_REASON of type + TY_REASON. +- The class redefines GET_TEXT. The text of the exception contains the reason. + +# 4. Contract +- Create the class {{P}}CLAIM_CHECKER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Public types of the class: + - TY_POLICY_ID TYPE c LENGTH 10. + - TY_AMOUNT TYPE p LENGTH 8 DECIMALS 2. + - TY_CLAIM, a structure with the components POLICY_ID (type TY_POLICY_ID), + VEHICLE_AGE (type i), DAMAGE (type TY_AMOUNT), DEDUCTIBLE (type TY_AMOUNT), + REPORT_DAYS (type i) and PHOTO_COUNT (type i). +- The public constant C_MAX_PAYOUT TYPE TY_AMOUNT has the value 10000.00. +- One public method: + CHECK_CLAIM + IMPORTING is_claim TYPE ty_claim + RETURNING VALUE(rv_payout) TYPE ty_amount + RAISING {{P}}CX_CLAIM_ERROR. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Raise the exception {{P}}CX_CLAIM_ERROR with +the reason of the first rule that fails. The reason is the text that the rule +shows. +1. The policy ID must start with the letter P. The other 9 characters must be + digits. Else raise the reason 'POLICY'. +2. REPORT_DAYS must be between 0 and 14. Else raise the reason 'LATE'. +3. VEHICLE_AGE must be between 0 and 12. Else raise the reason 'AGED'. +4. PHOTO_COUNT must be at least 2. Else raise the reason 'PHOTOS'. +5. DAMAGE must be greater than 0. Else raise the reason 'DAMAGE'. +6. DEDUCTIBLE must be between 0 and DAMAGE. Else raise the reason 'DEDUCT'. +7. The payout is DAMAGE minus DEDUCTIBLE. +8. If the payout is greater than C_MAX_PAYOUT, the payout is C_MAX_PAYOUT. + +# 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}}CX_CLAIM_ERROR. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1076/task.json b/tasks_gen/train/G1076/task.json new file mode 100644 index 0000000..2f799f8 --- /dev/null +++ b/tasks_gen/train/G1076/task.json @@ -0,0 +1,50 @@ +{ + "id": "G1076", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}CX_CLAIM_ERROR", + "file": "seed/cx_claim_error.clas.abap", + "description": "Claim exception with a reason and a message text" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CLAIM_CHECKER" + } + ], + "out_of_scope": [ + "{{P}}CX_CLAIM_ERROR" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}CLAIM_CHECK_HIDDEN", + "file": "hidden/claim_check_hidden.clas.abap", + "description": "Hidden tests for the car damage claim check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CLAIM_CHECKER", + "file": "reference/claim_checker.clas.abap", + "description": "Car damage claim checker", + "testclasses_file": "reference/claim_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1077/faulty/m0_water_bill.clas.abap b/tasks_gen/train/G1077/faulty/m0_water_bill.clas.abap new file mode 100644 index 0000000..45c6444 --- /dev/null +++ b/tasks_gen/train/G1077/faulty/m0_water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + - standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net < lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge + tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days * mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units > 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/faulty/m1_water_bill.clas.abap b/tasks_gen/train/G1077/faulty/m1_water_bill.clas.abap new file mode 100644 index 0000000..30834c5 --- /dev/null +++ b/tasks_gen/train/G1077/faulty/m1_water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + + standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net >= lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge + tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days * mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units > 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/faulty/m2_water_bill.clas.abap b/tasks_gen/train/G1077/faulty/m2_water_bill.clas.abap new file mode 100644 index 0000000..d44b681 --- /dev/null +++ b/tasks_gen/train/G1077/faulty/m2_water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + + standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net < lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge - tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days * mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units > 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/faulty/m3_water_bill.clas.abap b/tasks_gen/train/G1077/faulty/m3_water_bill.clas.abap new file mode 100644 index 0000000..362eb6a --- /dev/null +++ b/tasks_gen/train/G1077/faulty/m3_water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + + standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net < lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge + tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days / mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units > 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/faulty/m4_water_bill.clas.abap b/tasks_gen/train/G1077/faulty/m4_water_bill.clas.abap new file mode 100644 index 0000000..dfe8282 --- /dev/null +++ b/tasks_gen/train/G1077/faulty/m4_water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + + standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net < lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge + tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days * mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units <= 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/generation.json b/tasks_gen/train/G1077/generation.json new file mode 100644 index 0000000..c0d6a4d --- /dev/null +++ b/tasks_gen/train/G1077/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1077", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1077/hidden/water_bill_test.clas.abap b/tasks_gen/train/G1077/hidden/water_bill_test.clas.abap new file mode 100644 index 0000000..d7a1927 --- /dev/null +++ b/tasks_gen/train/G1077/hidden/water_bill_test.clas.abap @@ -0,0 +1,76 @@ +CLASS {{p}}water_bill_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + DATA mo_cut TYPE REF TO {{p}}water_bill. + + METHODS setup. + METHODS assert_bill + IMPORTING iv_consumption TYPE i + iv_days TYPE i + iv_expected TYPE ty_amount. + + METHODS zero_consumption FOR TESTING. + METHODS first_tier_only FOR TESTING. + METHODS second_tier FOR TESTING. + METHODS tier_boundary_50 FOR TESTING. + METHODS third_tier FOR TESTING. + METHODS sewage_rounding FOR TESTING. + METHODS standing_charge_only FOR TESTING. + METHODS below_minimum FOR TESTING. + METHODS large_consumption FOR TESTING. +ENDCLASS. + + +CLASS {{p}}water_bill_test IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}water_bill( ). + ENDMETHOD. + + METHOD assert_bill. + DATA(lv_actual) = CONV ty_amount( mo_cut->calculate( iv_consumption = iv_consumption + iv_days = iv_days ) ). + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = lv_actual + msg = |consumption { iv_consumption } cbm, { iv_days } days| ). + ENDMETHOD. + + METHOD zero_consumption. + assert_bill( iv_consumption = 0 iv_days = 30 iv_expected = '35.00' ). + ENDMETHOD. + + METHOD first_tier_only. + assert_bill( iv_consumption = 20 iv_days = 1 iv_expected = '68.91' ). + ENDMETHOD. + + METHOD second_tier. + assert_bill( iv_consumption = 30 iv_days = 1 iv_expected = '114.55' ). + ENDMETHOD. + + METHOD tier_boundary_50. + assert_bill( iv_consumption = 50 iv_days = 1 iv_expected = '205.83' ). + ENDMETHOD. + + METHOD third_tier. + assert_bill( iv_consumption = 60 iv_days = 1 iv_expected = '264.51' ). + ENDMETHOD. + + METHOD sewage_rounding. + assert_bill( iv_consumption = 22 iv_days = 1 iv_expected = '78.04' ). + ENDMETHOD. + + METHOD standing_charge_only. + assert_bill( iv_consumption = 0 iv_days = 100 iv_expected = '45.00' ). + ENDMETHOD. + + METHOD below_minimum. + assert_bill( iv_consumption = 4 iv_days = 30 iv_expected = '35.00' ). + ENDMETHOD. + + METHOD large_consumption. + assert_bill( iv_consumption = 120 iv_days = 90 iv_expected = '656.64' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/mutation.json b/tasks_gen/train/G1077/mutation.json new file mode 100644 index 0000000..5a46885 --- /dev/null +++ b/tasks_gen/train/G1077/mutation.json @@ -0,0 +1,86 @@ +{ + "task": "G1077", + "mutants": [ + { + "object": "{{P}}WATER_BILL", + "mutant": "line 45: + -> - (arith)", + "status": "killed", + "hidden": "2/9", + "failed_tests": [ + "FIRST_TIER_ONLY", + "LARGE_CONSUMPTION", + "SECOND_TIER", + "SEWAGE_ROUNDING", + "STANDING_CHARGE_ONLY", + "THIRD_TIER", + "TIER_BOUNDARY_50" + ] + }, + { + "object": "{{P}}WATER_BILL", + "mutant": "line 47: < -> >= (rel)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "BELOW_MINIMUM", + "FIRST_TIER_ONLY", + "LARGE_CONSUMPTION", + "SECOND_TIER", + "SEWAGE_ROUNDING", + "STANDING_CHARGE_ONLY", + "THIRD_TIER", + "TIER_BOUNDARY_50", + "ZERO_CONSUMPTION" + ] + }, + { + "object": "{{P}}WATER_BILL", + "mutant": "line 61: + -> - (arith)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "FIRST_TIER_ONLY", + "LARGE_CONSUMPTION", + "SECOND_TIER", + "SEWAGE_ROUNDING", + "THIRD_TIER", + "TIER_BOUNDARY_50" + ] + }, + { + "object": "{{P}}WATER_BILL", + "mutant": "line 78: * -> / (arith)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "BELOW_MINIMUM", + "FIRST_TIER_ONLY", + "LARGE_CONSUMPTION", + "SECOND_TIER", + "SEWAGE_ROUNDING", + "STANDING_CHARGE_ONLY", + "THIRD_TIER", + "TIER_BOUNDARY_50", + "ZERO_CONSUMPTION" + ] + }, + { + "object": "{{P}}WATER_BILL", + "mutant": "line 83: > -> <= (rel)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "FIRST_TIER_ONLY", + "LARGE_CONSUMPTION", + "SECOND_TIER", + "SEWAGE_ROUNDING", + "THIRD_TIER", + "TIER_BOUNDARY_50" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1077/reference/water_bill.clas.abap b/tasks_gen/train/G1077/reference/water_bill.clas.abap new file mode 100644 index 0000000..7f0b368 --- /dev/null +++ b/tasks_gen/train/G1077/reference/water_bill.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}water_bill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + + METHODS calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + + PRIVATE SECTION. + DATA mo_tariff TYPE REF TO {{p}}water_tariff. + + METHODS consumption_charge + IMPORTING iv_consumption TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS sewage_charge + IMPORTING iv_consumption_charge TYPE ty_amount + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS standing_charge + IMPORTING iv_days TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS tier_charge + IMPORTING iv_from TYPE i + iv_to TYPE i + iv_price TYPE {{p}}water_tariff=>ty_price + RETURNING VALUE(rv_charge) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}water_bill IMPLEMENTATION. + METHOD constructor. + mo_tariff = NEW {{p}}water_tariff( ). + ENDMETHOD. + + METHOD calculate. + DATA(lv_consumption_charge) = consumption_charge( iv_consumption ). + DATA lv_net TYPE ty_amount. + lv_net = lv_consumption_charge + + sewage_charge( lv_consumption_charge ) + + standing_charge( iv_days ). + DATA(lv_minimum) = mo_tariff->get_minimum_charge( ). + IF lv_net < lv_minimum. + rv_amount = lv_minimum. + ELSE. + rv_amount = lv_net. + ENDIF. + ENDMETHOD. + + METHOD consumption_charge. + DATA(lt_tiers) = mo_tariff->get_tiers( ). + DATA lv_from TYPE i. + LOOP AT lt_tiers INTO DATA(ls_tier). + DATA(lv_to) = COND i( WHEN ls_tier-limit_cbm = 0 OR ls_tier-limit_cbm > iv_consumption + THEN iv_consumption + ELSE ls_tier-limit_cbm ). + rv_charge = rv_charge + tier_charge( iv_from = lv_from + iv_to = lv_to + iv_price = ls_tier-price_cbm ). + lv_from = lv_to. + IF lv_from >= iv_consumption. + EXIT. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD sewage_charge. + DATA lv_product TYPE p LENGTH 16 DECIMALS 4. + lv_product = iv_consumption_charge * mo_tariff->get_sewage_percent( ). + rv_charge = lv_product / 100. + ENDMETHOD. + + METHOD standing_charge. + rv_charge = iv_days * mo_tariff->get_standing_charge( ). + ENDMETHOD. + + METHOD tier_charge. + DATA(lv_units) = iv_to - iv_from. + IF lv_units > 0. + rv_charge = lv_units * iv_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/reference/water_bill.testclasses.abap b/tasks_gen/train/G1077/reference/water_bill.testclasses.abap new file mode 100644 index 0000000..6da3324 --- /dev/null +++ b/tasks_gen/train/G1077/reference/water_bill.testclasses.abap @@ -0,0 +1,51 @@ +CLASS ltc_water_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + DATA mo_cut TYPE REF TO {{p}}water_bill. + + METHODS setup. + METHODS assert_bill + IMPORTING iv_consumption TYPE i + iv_days TYPE i + iv_expected TYPE ty_amount. + + METHODS first_tier_only FOR TESTING. + METHODS third_tier FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS standing_charge_only FOR TESTING. + METHODS sewage_is_rounded FOR TESTING. +ENDCLASS. + + +CLASS ltc_water_bill IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}water_bill( ). + ENDMETHOD. + + METHOD assert_bill. + DATA(lv_actual) = CONV ty_amount( mo_cut->calculate( iv_consumption = iv_consumption + iv_days = iv_days ) ). + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = lv_actual ). + ENDMETHOD. + + METHOD first_tier_only. + assert_bill( iv_consumption = 20 iv_days = 1 iv_expected = '68.91' ). + ENDMETHOD. + + METHOD third_tier. + assert_bill( iv_consumption = 60 iv_days = 1 iv_expected = '264.51' ). + ENDMETHOD. + + METHOD minimum_charge. + assert_bill( iv_consumption = 4 iv_days = 30 iv_expected = '35.00' ). + ENDMETHOD. + + METHOD standing_charge_only. + assert_bill( iv_consumption = 0 iv_days = 100 iv_expected = '45.00' ). + ENDMETHOD. + + METHOD sewage_is_rounded. + assert_bill( iv_consumption = 22 iv_days = 1 iv_expected = '78.04' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/seed/water_tariff.clas.abap b/tasks_gen/train/G1077/seed/water_tariff.clas.abap new file mode 100644 index 0000000..6ffe4db --- /dev/null +++ b/tasks_gen/train/G1077/seed/water_tariff.clas.abap @@ -0,0 +1,44 @@ +CLASS {{p}}water_tariff DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + TYPES: + BEGIN OF ty_tier, + limit_cbm TYPE i, + price_cbm TYPE ty_price, + END OF ty_tier, + tt_tiers TYPE STANDARD TABLE OF ty_tier WITH EMPTY KEY. + + METHODS get_tiers + RETURNING VALUE(rt_tiers) TYPE tt_tiers. + + METHODS get_standing_charge + RETURNING VALUE(rv_charge) TYPE ty_price. + + METHODS get_sewage_percent + RETURNING VALUE(rv_percent) TYPE ty_price. + + METHODS get_minimum_charge + RETURNING VALUE(rv_charge) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}water_tariff IMPLEMENTATION. + METHOD get_tiers. + rt_tiers = VALUE #( ( limit_cbm = 20 price_cbm = '2.10' ) + ( limit_cbm = 50 price_cbm = '2.80' ) + ( limit_cbm = 0 price_cbm = '3.60' ) ). + ENDMETHOD. + + METHOD get_standing_charge. + rv_charge = '0.45'. + ENDMETHOD. + + METHOD get_sewage_percent. + rv_percent = '63.00'. + ENDMETHOD. + + METHOD get_minimum_charge. + rv_charge = '35.00'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1077/spec.md b/tasks_gen/train/G1077/spec.md new file mode 100644 index 0000000..c5bfd22 --- /dev/null +++ b/tasks_gen/train/G1077/spec.md @@ -0,0 +1,62 @@ +# 1. Goal +A water utility bills a household for one quarter. The billing program calls the +class {{P}}WATER_BILL for each meter reading. The class calculates the amount of +the bill in euro. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}WATER_TARIFF exists in package $TMP. It is active. It gives the + tariff data of the utility. Read its definition before you start. +- The tariff data contains a list of price tiers, the standing charge for one + day, the sewage percentage, and the minimum charge. +- All objects are in package $TMP. + +# 4. Contract +- Create the class {{P}}WATER_BILL in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has the public type TY_AMOUNT. TY_AMOUNT is a packed number with + two decimal places. +- The class has this public method and no other public method: + calculate + IMPORTING iv_consumption TYPE i + iv_days TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. +- iv_consumption is the consumption of the household in cubic metres. +- iv_days is the number of days of the billing period. + +# 5. Business rules +Use the values of the tariff in {{P}}WATER_TARIFF. +1. Consumption charge: + - The tariff gives the price tiers in ascending order. + - The tiers are cumulative. The upper limit of a tier is a total consumption, + not the width of the tier. + - A tier covers the consumption up to its upper limit. + - The tier with the upper limit 0 is the last tier. It covers all the + consumption above the upper limit of the previous tier. + - Multiply the consumption of each tier by the price of that tier. + - The consumption charge is the sum of these products. +2. Sewage charge: it is the sewage percentage of the consumption charge. The + sewage percentage is a number of percent (50 means 50 percent). Round the + sewage charge to two decimal places. +3. Standing charge: it is the number of days multiplied by the standing charge + of the tariff for one day. +4. The net amount is the consumption charge plus the sewage charge plus the + standing charge. +5. If the net amount is below the minimum charge of the tariff, the amount of + the bill is the minimum charge. +6. Else the amount of the bill is the net amount. +7. The amount of the bill has two decimal places. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. Pass large parameters by + reference. +- Out of scope: do not change {{P}}WATER_TARIFF. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1077/task.json b/tasks_gen/train/G1077/task.json new file mode 100644 index 0000000..78b89be --- /dev/null +++ b/tasks_gen/train/G1077/task.json @@ -0,0 +1,50 @@ +{ + "id": "G1077", + "category": "F", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}WATER_TARIFF", + "file": "seed/water_tariff.clas.abap", + "description": "Tariff provider of the water utility" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}WATER_BILL" + } + ], + "out_of_scope": [ + "{{P}}WATER_TARIFF" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}WATER_BILL_TEST", + "file": "hidden/water_bill_test.clas.abap", + "description": "Hidden tests for the water bill calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}WATER_BILL", + "file": "reference/water_bill.clas.abap", + "description": "Water bill calculator", + "testclasses_file": "reference/water_bill.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1078/faulty/m0_sewer_fee.func.abap b/tasks_gen/train/G1078/faulty/m0_sewer_fee.func.abap new file mode 100644 index 0000000..1d8ff9b --- /dev/null +++ b/tasks_gen/train/G1078/faulty/m0_sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_true. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer = abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres > 1000. + lv_steps = ( iv_tank_litres - 1001 ) DIV c_step_litres + 1. + cv_fee = cv_fee + lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/faulty/m1_sewer_fee.func.abap b/tasks_gen/train/G1078/faulty/m1_sewer_fee.func.abap new file mode 100644 index 0000000..b483661 --- /dev/null +++ b/tasks_gen/train/G1078/faulty/m1_sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_false. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer <> abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres > 1000. + lv_steps = ( iv_tank_litres - 1001 ) DIV c_step_litres + 1. + cv_fee = cv_fee + lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/faulty/m2_sewer_fee.func.abap b/tasks_gen/train/G1078/faulty/m2_sewer_fee.func.abap new file mode 100644 index 0000000..4a37b04 --- /dev/null +++ b/tasks_gen/train/G1078/faulty/m2_sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_false. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer = abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres <= 1000. + lv_steps = ( iv_tank_litres - 1001 ) DIV c_step_litres + 1. + cv_fee = cv_fee + lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/faulty/m3_sewer_fee.func.abap b/tasks_gen/train/G1078/faulty/m3_sewer_fee.func.abap new file mode 100644 index 0000000..63b941c --- /dev/null +++ b/tasks_gen/train/G1078/faulty/m3_sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_false. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer = abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres > 1000. + lv_steps = ( iv_tank_litres - 1002 ) DIV c_step_litres + 1. + cv_fee = cv_fee + lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/faulty/m4_sewer_fee.func.abap b/tasks_gen/train/G1078/faulty/m4_sewer_fee.func.abap new file mode 100644 index 0000000..c1bb4eb --- /dev/null +++ b/tasks_gen/train/G1078/faulty/m4_sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_false. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer = abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres > 1000. + lv_steps = ( iv_tank_litres - 1001 ) DIV c_step_litres + 1. + cv_fee = cv_fee - lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/generation.json b/tasks_gen/train/G1078/generation.json new file mode 100644 index 0000000..504c1b8 --- /dev/null +++ b/tasks_gen/train/G1078/generation.json @@ -0,0 +1,30 @@ +{ + "id": "G1078", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.68, rules 0.62, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.66, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0156 (similarity spec 0.65, rules 0.60, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1078/hidden/sewer_hidden.clas.abap b/tasks_gen/train/G1078/hidden/sewer_hidden.clas.abap new file mode 100644 index 0000000..b2940e6 --- /dev/null +++ b/tasks_gen/train/G1078/hidden/sewer_hidden.clas.abap @@ -0,0 +1,97 @@ +CLASS {{p}}sewer_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_litres TYPE i + iv_km TYPE i DEFAULT 0 + iv_mains TYPE abap_bool DEFAULT abap_false + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS base_fee FOR TESTING. + METHODS volume_steps FOR TESTING. + METHODS travel_fee FOR TESTING. + METHODS travel_cap FOR TESTING. + METHODS large_tank FOR TESTING. + METHODS fee_cap FOR TESTING. + METHODS mains_sewer_skips FOR TESTING. + METHODS skip_false FOR TESTING. + METHODS invalid_litres FOR TESTING. +ENDCLASS. + + +CLASS {{p}}sewer_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}SEWER_FEE' + EXPORTING + iv_tank_litres = iv_litres + iv_distance_km = iv_km + iv_mains_sewer = iv_mains + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_litres = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD base_fee. + call( EXPORTING iv_litres = 800 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 75 ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD volume_steps. + call( EXPORTING iv_litres = 1001 IMPORTING ev_fee = DATA(lv_a) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 95 ) act = lv_a ). + call( EXPORTING iv_litres = 1500 IMPORTING ev_fee = DATA(lv_b) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 95 ) act = lv_b ). + call( EXPORTING iv_litres = 1501 IMPORTING ev_fee = DATA(lv_c) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 115 ) act = lv_c ). + ENDMETHOD. + + METHOD travel_fee. + call( EXPORTING iv_litres = 1000 iv_km = 10 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 90 ) act = lv_fee ). + ENDMETHOD. + + METHOD travel_cap. + call( EXPORTING iv_litres = 1000 iv_km = 50 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 135 ) act = lv_fee ). + ENDMETHOD. + + METHOD large_tank. + call( EXPORTING iv_litres = 20000 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 835 ) act = lv_fee ). + ENDMETHOD. + + METHOD fee_cap. + call( EXPORTING iv_litres = 30000 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 900 ) act = lv_fee ). + ENDMETHOD. + + METHOD mains_sewer_skips. + call( EXPORTING iv_litres = 2000 iv_km = 20 iv_mains = abap_true iv_fee = 7 + IMPORTING ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_fee ). + ENDMETHOD. + + METHOD skip_false. + call( EXPORTING iv_litres = 1000 iv_km = 5 + IMPORTING ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD invalid_litres. + call( EXPORTING iv_litres = 0 IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1078/mutation.json b/tasks_gen/train/G1078/mutation.json new file mode 100644 index 0000000..124eeae --- /dev/null +++ b/tasks_gen/train/G1078/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1078", + "mutants": [ + { + "object": "{{P}}SEWER_FEE", + "mutant": "line 22: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "BASE_FEE", + "SKIP_FALSE" + ] + }, + { + "object": "{{P}}SEWER_FEE", + "mutant": "line 28: = -> <> (eq)", + "status": "killed", + "hidden": "1/9", + "failed_tests": [ + "BASE_FEE", + "FEE_CAP", + "LARGE_TANK", + "MAINS_SEWER_SKIPS", + "SKIP_FALSE", + "TRAVEL_CAP", + "TRAVEL_FEE", + "VOLUME_STEPS" + ] + }, + { + "object": "{{P}}SEWER_FEE", + "mutant": "line 34: > -> <= (rel)", + "status": "killed", + "hidden": "6/9", + "failed_tests": [ + "FEE_CAP", + "LARGE_TANK", + "VOLUME_STEPS" + ] + }, + { + "object": "{{P}}SEWER_FEE", + "mutant": "line 35: 1001 -> 1002 (const)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "VOLUME_STEPS" + ] + }, + { + "object": "{{P}}SEWER_FEE", + "mutant": "line 36: + -> - (arith)", + "status": "killed", + "hidden": "6/9", + "failed_tests": [ + "FEE_CAP", + "LARGE_TANK", + "VOLUME_STEPS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1078/reference/sewer_fee.func.abap b/tasks_gen/train/G1078/reference/sewer_fee.func.abap new file mode 100644 index 0000000..fd1917f --- /dev/null +++ b/tasks_gen/train/G1078/reference/sewer_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}sewer_fee + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + CONSTANTS c_base_fee TYPE decfloat34 VALUE '75'. + CONSTANTS c_step_fee TYPE decfloat34 VALUE '20'. + CONSTANTS c_step_litres TYPE i VALUE 500. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS c_travel_max TYPE decfloat34 VALUE '60'. + CONSTANTS c_fee_max TYPE decfloat34 VALUE '900'. + DATA lv_steps TYPE i. + DATA lv_travel TYPE decfloat34. + + ev_skip = abap_false. + + IF iv_tank_litres < 1. + RAISE invalid_litres. + ENDIF. + + IF iv_mains_sewer = abap_true. + ev_skip = abap_true. + RETURN. + ENDIF. + + cv_fee = c_base_fee. + IF iv_tank_litres > 1000. + lv_steps = ( iv_tank_litres - 1001 ) DIV c_step_litres + 1. + cv_fee = cv_fee + lv_steps * c_step_fee. + ENDIF. + + IF iv_distance_km > 0. + lv_travel = iv_distance_km * c_km_rate. + IF lv_travel > c_travel_max. + lv_travel = c_travel_max. + ENDIF. + cv_fee = cv_fee + lv_travel. + ENDIF. + + IF cv_fee > c_fee_max. + cv_fee = c_fee_max. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/reference/sewer_test.clas.abap b/tasks_gen/train/G1078/reference/sewer_test.clas.abap new file mode 100644 index 0000000..1d6309b --- /dev/null +++ b/tasks_gen/train/G1078/reference/sewer_test.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}sewer_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_litres TYPE i + iv_km TYPE i DEFAULT 0 + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS base_fee FOR TESTING. + METHODS volume_steps FOR TESTING. + METHODS travel_fee FOR TESTING. + METHODS fee_cap FOR TESTING. + METHODS mains_sewer FOR TESTING. + METHODS invalid_litres FOR TESTING. +ENDCLASS. + + +CLASS {{p}}sewer_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}SEWER_FEE' + EXPORTING + iv_tank_litres = iv_litres + iv_distance_km = iv_km + iv_mains_sewer = abap_false + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_litres = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD base_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 75 ) + act = fee_for( iv_litres = 1000 ) ). + ENDMETHOD. + + METHOD volume_steps. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 95 ) + act = fee_for( iv_litres = 1500 ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 115 ) + act = fee_for( iv_litres = 1501 ) ). + ENDMETHOD. + + METHOD travel_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 90 ) + act = fee_for( iv_litres = 1000 iv_km = 10 ) ). + ENDMETHOD. + + METHOD fee_cap. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 900 ) + act = fee_for( iv_litres = 30000 ) ). + ENDMETHOD. + + METHOD mains_sewer. + DATA lv_fee TYPE decfloat34 VALUE 7. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}SEWER_FEE' + EXPORTING + iv_tank_litres = 2000 + iv_distance_km = 20 + iv_mains_sewer = abap_true + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = lv_fee. + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_litres. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}SEWER_FEE' + EXPORTING + iv_tank_litres = 0 + iv_distance_km = 0 + iv_mains_sewer = abap_false + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_litres = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1078/seed/sewer_sample.func.abap b/tasks_gen/train/G1078/seed/sewer_sample.func.abap new file mode 100644 index 0000000..ca19a15 --- /dev/null +++ b/tasks_gen/train/G1078/seed/sewer_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}sewer_sample + IMPORTING + iv_tank_litres TYPE i + iv_distance_km TYPE i + iv_mains_sewer TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_litres. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1078/spec.md b/tasks_gen/train/G1078/spec.md new file mode 100644 index 0000000..494806d --- /dev/null +++ b/tasks_gen/train/G1078/spec.md @@ -0,0 +1,54 @@ +# 1. Goal +Calculate the emptying fee for one septic tank. The depot program calls one +function module for each emptying job. The function module must have the same +interface as the sample function module, because the depot program calls all fee +modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_SEWER exists in package $TMP. +- Function module {{P}}SEWER_SAMPLE exists in this group. It is the sample fee + module. Its interface is the interface that all fee modules must use. +- A drainage company empties septic tanks. The depot program sends the size of + the tank, the distance from the depot, and the connection type of the + property. + +# 4. Contract +- Create the function module {{P}}SEWER_FEE in function group {{P}}FG_SEWER. +- Use exactly the interface of {{P}}SEWER_SAMPLE: + - importing iv_tank_litres type i + - importing iv_distance_km type i + - importing iv_mains_sewer type abap_bool + - exporting ev_skip type abap_bool + - changing cv_fee type decfloat34 + - exception invalid_litres + +# 5. Business rules +1. If iv_tank_litres is less than 1, raise the exception INVALID_LITRES. +2. Set the skip flag to false. Rule 4 is the only exception. +3. The base fee is 75.00. The base fee covers the first 1000 litres of the + tank. +4. If the property is connected to the mains sewer, no emptying is necessary. + Set the skip flag to true and do not change the fee. Do not apply rules 5 + to 7. +5. Each started 500 litres above 1000 litres adds 20.00 to the fee. Example: + 1000 litres add 0.00, 1001 litres add 20.00, 1500 litres add 20.00, and + 1501 litres add 40.00. +6. The travel fee is 1.50 for each kilometre of iv_distance_km. The travel fee + is not more than 60.00. +7. The fee is the base fee of rule 3 plus the volume fee of rule 5 plus the + travel fee of rule 6. The fee is not more than 900.00. + +# 6. Constraints +- Release target: 7.40 SP05. Use only syntax that exists in 7.40 SP05. Do not + use syntax that is newer than 7.40 SP05. +- Out of scope: do not change {{P}}SEWER_SAMPLE. +- Coding standards: Clean ABAP. A method has less than 40 statements. +- Out of scope: no database access, no ALV, no dynpro. + +# 7. Acceptance +- The function module {{P}}SEWER_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1078/task.json b/tasks_gen/train/G1078/task.json new file mode 100644 index 0000000..a0db99f --- /dev/null +++ b/tasks_gen/train/G1078/task.json @@ -0,0 +1,85 @@ +{ + "id": "G1078", + "category": "G", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v740sp05", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_SEWER", + "description": "Fee modules of the drainage company" + }, + { + "type": "FUNC", + "name": "{{P}}SEWER_SAMPLE", + "functionGroup": "{{P}}FG_SEWER", + "file": "seed/sewer_sample.func.abap", + "description": "Sample fee module with the interface of all fee modules" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}SEWER_FEE", + "functionGroup": "{{P}}FG_SEWER", + "params": [ + { + "name": "iv_tank_litres", + "type": "i" + }, + { + "name": "iv_distance_km", + "type": "i" + }, + { + "name": "iv_mains_sewer", + "type": "abap_bool" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}SEWER_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SEWER_HIDDEN", + "file": "hidden/sewer_hidden.clas.abap", + "description": "Hidden tests for {{P}}SEWER_FEE" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}SEWER_FEE", + "functionGroup": "{{P}}FG_SEWER", + "file": "reference/sewer_fee.func.abap", + "description": "Emptying fee module" + }, + { + "type": "CLAS", + "name": "{{P}}SEWER_TEST", + "file": "reference/sewer_test.clas.abap", + "description": "Own tests for {{P}}SEWER_FEE" + } + ], + "craft_checks": [ + "method_length", + "no_newer_syntax" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1079/generation.json b/tasks_gen/train/G1079/generation.json new file mode 100644 index 0000000..fcdfb4d --- /dev/null +++ b/tasks_gen/train/G1079/generation.json @@ -0,0 +1,23 @@ +{ + "id": "G1079", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/soil_evaluator.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1079/reference/soil_evaluator.clas.abap b/tasks_gen/train/G1079/reference/soil_evaluator.clas.abap new file mode 100644 index 0000000..dc7a7ec --- /dev/null +++ b/tasks_gen/train/G1079/reference/soil_evaluator.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}soil_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_ph TYPE p LENGTH 3 DECIMALS 1, + ty_nutrient TYPE i, + ty_status TYPE c LENGTH 1, + ty_grade TYPE c LENGTH 1, + BEGIN OF ty_sample, + ph TYPE ty_ph, + nutrient TYPE ty_nutrient, + END OF ty_sample, + tt_sample TYPE STANDARD TABLE OF ty_sample WITH EMPTY KEY, + BEGIN OF ty_result, + status TYPE ty_status, + avg_ph TYPE ty_ph, + avg_nutrient TYPE ty_nutrient, + grade TYPE ty_grade, + END OF ty_result. + CONSTANTS: + c_status_ok TYPE ty_status VALUE 'O', + c_status_error TYPE ty_status VALUE 'E', + c_grade_a TYPE ty_grade VALUE 'A', + c_grade_b TYPE ty_grade VALUE 'B', + c_grade_c TYPE ty_grade VALUE 'C'. + METHODS evaluate + IMPORTING it_samples TYPE tt_sample + RETURNING VALUE(rs_result) TYPE ty_result. + PRIVATE SECTION. + CONSTANTS: + c_ph_min TYPE ty_ph VALUE '0.0', + c_ph_max TYPE ty_ph VALUE '14.0', + c_ph_a_min TYPE ty_ph VALUE '6.0', + c_ph_a_max TYPE ty_ph VALUE '7.5', + c_ph_b_min TYPE ty_ph VALUE '5.0', + c_ph_b_max TYPE ty_ph VALUE '8.0', + c_nutr_a_min TYPE ty_nutrient VALUE 50, + c_nutr_b_min TYPE ty_nutrient VALUE 30. + METHODS has_invalid_ph + IMPORTING it_samples TYPE tt_sample + RETURNING VALUE(rv_invalid) TYPE abap_bool. + METHODS average_ph + IMPORTING it_samples TYPE tt_sample + RETURNING VALUE(rv_avg) TYPE ty_ph. + METHODS average_nutrient + IMPORTING it_samples TYPE tt_sample + RETURNING VALUE(rv_avg) TYPE ty_nutrient. + METHODS determine_grade + IMPORTING iv_avg_ph TYPE ty_ph + iv_avg_nutrient TYPE ty_nutrient + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + +CLASS {{p}}soil_evaluator IMPLEMENTATION. + METHOD evaluate. + IF it_samples IS INITIAL. + rs_result-status = c_status_error. + RETURN. + ENDIF. + IF has_invalid_ph( it_samples ) = abap_true. + rs_result-status = c_status_error. + RETURN. + ENDIF. + rs_result-avg_ph = average_ph( it_samples ). + rs_result-avg_nutrient = average_nutrient( it_samples ). + rs_result-grade = determine_grade( iv_avg_ph = rs_result-avg_ph + iv_avg_nutrient = rs_result-avg_nutrient ). + rs_result-status = c_status_ok. + ENDMETHOD. + + METHOD has_invalid_ph. + LOOP AT it_samples INTO DATA(ls_sample). + IF ls_sample-ph < c_ph_min OR ls_sample-ph > c_ph_max. + rv_invalid = abap_true. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD average_ph. + DATA lv_sum TYPE p LENGTH 9 DECIMALS 1. + LOOP AT it_samples INTO DATA(ls_sample). + lv_sum = lv_sum + ls_sample-ph. + ENDLOOP. + rv_avg = lv_sum / lines( it_samples ). + ENDMETHOD. + + METHOD average_nutrient. + DATA lv_sum TYPE i. + LOOP AT it_samples INTO DATA(ls_sample). + lv_sum = lv_sum + ls_sample-nutrient. + ENDLOOP. + rv_avg = lv_sum / lines( it_samples ). + ENDMETHOD. + + METHOD determine_grade. + IF iv_avg_ph >= c_ph_a_min AND iv_avg_ph <= c_ph_a_max + AND iv_avg_nutrient >= c_nutr_a_min. + rv_grade = c_grade_a. + ELSEIF iv_avg_ph >= c_ph_b_min AND iv_avg_ph <= c_ph_b_max + AND iv_avg_nutrient >= c_nutr_b_min. + rv_grade = c_grade_b. + ELSE. + rv_grade = c_grade_c. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1079/reference/soil_evaluator.testclasses.abap b/tasks_gen/train/G1079/reference/soil_evaluator.testclasses.abap new file mode 100644 index 0000000..712dc94 --- /dev/null +++ b/tasks_gen/train/G1079/reference/soil_evaluator.testclasses.abap @@ -0,0 +1,60 @@ +CLASS ltc_soil_evaluator DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}soil_evaluator. + METHODS setup. + METHODS valid_samples_grade_a FOR TESTING. + METHODS valid_samples_grade_b FOR TESTING. + METHODS valid_samples_grade_c FOR TESTING. + METHODS invalid_ph FOR TESTING. + METHODS empty_table FOR TESTING. +ENDCLASS. + +CLASS ltc_soil_evaluator IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}soil_evaluator( ). + ENDMETHOD. + + METHOD valid_samples_grade_a. + DATA(lt_samples) = VALUE {{p}}soil_evaluator=>tt_sample( + ( ph = '6.5' nutrient = 60 ) + ( ph = '7.0' nutrient = 55 ) ). + DATA(ls_result) = mo_cut->evaluate( lt_samples ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_status_ok act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = '6.8' act = ls_result-avg_ph ). + cl_abap_unit_assert=>assert_equals( exp = 58 act = ls_result-avg_nutrient ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_grade_a act = ls_result-grade ). + ENDMETHOD. + + METHOD valid_samples_grade_b. + DATA(lt_samples) = VALUE {{p}}soil_evaluator=>tt_sample( + ( ph = '5.5' nutrient = 35 ) + ( ph = '6.0' nutrient = 40 ) ). + DATA(ls_result) = mo_cut->evaluate( lt_samples ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_status_ok act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_grade_b act = ls_result-grade ). + ENDMETHOD. + + METHOD valid_samples_grade_c. + DATA(lt_samples) = VALUE {{p}}soil_evaluator=>tt_sample( + ( ph = '4.0' nutrient = 10 ) ). + DATA(ls_result) = mo_cut->evaluate( lt_samples ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_status_ok act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_grade_c act = ls_result-grade ). + ENDMETHOD. + + METHOD invalid_ph. + DATA(lt_samples) = VALUE {{p}}soil_evaluator=>tt_sample( + ( ph = '15.0' nutrient = 50 ) ). + DATA(ls_result) = mo_cut->evaluate( lt_samples ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_status_error act = ls_result-status ). + cl_abap_unit_assert=>assert_initial( act = ls_result-avg_ph ). + cl_abap_unit_assert=>assert_initial( act = ls_result-avg_nutrient ). + cl_abap_unit_assert=>assert_initial( act = ls_result-grade ). + ENDMETHOD. + + METHOD empty_table. + DATA(lt_samples) = VALUE {{p}}soil_evaluator=>tt_sample( ). + DATA(ls_result) = mo_cut->evaluate( lt_samples ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}soil_evaluator=>c_status_error act = ls_result-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1079/spec.md b/tasks_gen/train/G1079/spec.md new file mode 100644 index 0000000..03f05e0 --- /dev/null +++ b/tasks_gen/train/G1079/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +Evaluate soil samples from farms. A laboratory program calls the evaluation before it gives a fertilizer recommendation. The program needs the average pH, the average nutrient value, and a grade for each delivery of samples. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SOIL_EVALUATOR is new. It is in package $TMP. +- The laboratory program collects the samples of one delivery in a table. +- Each sample has a pH value and a nutrient value. + +# 4. Contract +- Create the class {{P}}SOIL_EVALUATOR in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Define these public types: + - TY_PH: type P, length 3, decimals 1. + - TY_NUTRIENT: type I. + - TY_STATUS: type C, length 1. + - TY_GRADE: type C, length 1. + - TY_SAMPLE: a structure with PH (type TY_PH) and NUTRIENT (type TY_NUTRIENT). + - TT_SAMPLE: a standard table of TY_SAMPLE with empty key. + - TY_RESULT: a structure with STATUS (type TY_STATUS), AVG_PH (type TY_PH), AVG_NUTRIENT (type TY_NUTRIENT), and GRADE (type TY_GRADE). +- Define these public constants: + - C_STATUS_OK TYPE TY_STATUS VALUE 'O'. + - C_STATUS_ERROR TYPE TY_STATUS VALUE 'E'. + - C_GRADE_A TYPE TY_GRADE VALUE 'A'. + - C_GRADE_B TYPE TY_GRADE VALUE 'B'. + - C_GRADE_C TYPE TY_GRADE VALUE 'C'. +- Define this public method: + - EVALUATE IMPORTING it_samples TYPE tt_sample RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. If any sample has a pH less than 0 or greater than 14, set STATUS = C_STATUS_ERROR. Leave the other fields of the result initial. Return the result. +2. Calculate the average pH and the average nutrient of all samples. Round the average pH to one decimal place. Round the average nutrient to the nearest integer. +3. Determine the grade: + - If the average pH is between 6.0 and 7.5 inclusive and the average nutrient is 50 or more, set GRADE = C_GRADE_A. + - Else if the average pH is between 5.0 and 8.0 inclusive and the average nutrient is 30 or more, set GRADE = C_GRADE_B. + - Else set GRADE = C_GRADE_C. +4. Set STATUS = C_STATUS_OK. + +# 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 other objects. + +# 7. Acceptance +- The class is active and has no syntax error. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1079/task.json b/tasks_gen/train/G1079/task.json new file mode 100644 index 0000000..e107cc1 --- /dev/null +++ b/tasks_gen/train/G1079/task.json @@ -0,0 +1,55 @@ +{ + "id": "G1079", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The business rules do not state what the class returns when the input table is empty.", + "keywords": [ + [ + "empty", + "table" + ], + [ + "empty", + "input" + ], + [ + "missing", + "rule" + ], + [ + "undefined", + "empty" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SOIL_EVALUATOR" + } + ], + "out_of_scope": [], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SOIL_EVALUATOR", + "file": "reference/soil_evaluator.clas.abap", + "description": "Soil evaluator", + "testclasses_file": "reference/soil_evaluator.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1080/faulty/m0_i_vend_state.ddls.asddls b/tasks_gen/train/G1080/faulty/m0_i_vend_state.ddls.asddls new file mode 100644 index 0000000..78d2172 --- /dev/null +++ b/tasks_gen/train/G1080/faulty/m0_i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + left outer join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 1 + else 0 + end ) as abap.int4 ) as empty_slots, + division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) = 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) < 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/faulty/m1_i_vend_state.ddls.asddls b/tasks_gen/train/G1080/faulty/m1_i_vend_state.ddls.asddls new file mode 100644 index 0000000..6f7f552 --- /dev/null +++ b/tasks_gen/train/G1080/faulty/m1_i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + inner join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 2 + else 0 + end ) as abap.int4 ) as empty_slots, + division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) = 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) < 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/faulty/m2_i_vend_state.ddls.asddls b/tasks_gen/train/G1080/faulty/m2_i_vend_state.ddls.asddls new file mode 100644 index 0000000..c0b72dc --- /dev/null +++ b/tasks_gen/train/G1080/faulty/m2_i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + inner join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 1 + else 0 + end ) as abap.int4 ) as empty_slots, + division( max( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) = 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) < 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/faulty/m3_i_vend_state.ddls.asddls b/tasks_gen/train/G1080/faulty/m3_i_vend_state.ddls.asddls new file mode 100644 index 0000000..d39ea02 --- /dev/null +++ b/tasks_gen/train/G1080/faulty/m3_i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + inner join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 1 + else 0 + end ) as abap.int4 ) as empty_slots, + division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) <> 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) < 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/faulty/m4_i_vend_state.ddls.asddls b/tasks_gen/train/G1080/faulty/m4_i_vend_state.ddls.asddls new file mode 100644 index 0000000..8f0c484 --- /dev/null +++ b/tasks_gen/train/G1080/faulty/m4_i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + inner join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 1 + else 0 + end ) as abap.int4 ) as empty_slots, + division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) = 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) >= 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/generation.json b/tasks_gen/train/G1080/generation.json new file mode 100644 index 0000000..9fe44e4 --- /dev/null +++ b/tasks_gen/train/G1080/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1080", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1080/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1080/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..3627178 --- /dev/null +++ b/tasks_gen/train/G1080/hidden/t16_hidden.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}t16_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS slot_counts FOR TESTING. + METHODS empty_slots FOR TESTING. + METHODS fill_rate_decimals FOR TESTING. + METHODS fill_rate_half FOR TESTING. + METHODS status_empty FOR TESTING. + METHODS status_low FOR TESTING. + METHODS status_ok FOR TESTING. + METHODS status_at_limit FOR TESTING. + METHODS machine_without_slots FOR TESTING. + METHODS location_upper FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_VEND_STATE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_mach TYPE STANDARD TABLE OF {{p}}vmach WITH EMPTY KEY. + DATA lt_slot TYPE STANDARD TABLE OF {{p}}vslot WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_mach = VALUE #( ( machine_id = 'M1' location = 'Hall A' ) + ( machine_id = 'M2' location = 'Bay 2' ) + ( machine_id = 'M3' location = 'Depot C' ) + ( machine_id = 'M4' location = 'Kiosk D' ) + ( machine_id = 'M5' location = 'Gate E' ) + ( machine_id = 'M6' location = 'Pier F' ) ). + lt_slot = VALUE #( ( machine_id = 'M1' slot_no = 1 product = 'COLA' fill_level = 3 max_units = 10 ) + ( machine_id = 'M1' slot_no = 2 product = 'WATER' fill_level = 0 max_units = 10 ) + ( machine_id = 'M2' slot_no = 1 product = 'CHIPS' fill_level = 0 max_units = 5 ) + ( machine_id = 'M2' slot_no = 2 product = 'NUTS' fill_level = 0 max_units = 5 ) + ( machine_id = 'M3' slot_no = 1 product = 'COLA' fill_level = 5 max_units = 10 ) + ( machine_id = 'M3' slot_no = 2 product = 'JUICE' fill_level = 5 max_units = 10 ) + ( machine_id = 'M5' slot_no = 1 product = 'TEA' fill_level = 5 max_units = 20 ) + ( machine_id = 'M6' slot_no = 1 product = 'MILK' fill_level = 1 max_units = 8 ) ). + go_env->insert_test_data( lt_mach ). + go_env->insert_test_data( lt_slot ). + ENDMETHOD. + + METHOD slot_counts. + SELECT SINGLE slot_count FROM {{p}}i_vend_state WHERE machine_id = 'M1' INTO @DATA(lv_two). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_two ). + SELECT SINGLE slot_count FROM {{p}}i_vend_state WHERE machine_id = 'M5' INTO @DATA(lv_one). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_one ). + ENDMETHOD. + + METHOD empty_slots. + SELECT SINGLE empty_slots FROM {{p}}i_vend_state WHERE machine_id = 'M1' INTO @DATA(lv_one). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_one ). + SELECT SINGLE empty_slots FROM {{p}}i_vend_state WHERE machine_id = 'M2' INTO @DATA(lv_two). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_two ). + SELECT SINGLE empty_slots FROM {{p}}i_vend_state WHERE machine_id = 'M3' INTO @DATA(lv_none). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_none ). + ENDMETHOD. + + METHOD fill_rate_decimals. + SELECT SINGLE fill_rate FROM {{p}}i_vend_state WHERE machine_id = 'M6' INTO @DATA(lv_rate). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.5' ) + act = CONV decfloat34( lv_rate ) ). + ENDMETHOD. + + METHOD fill_rate_half. + SELECT SINGLE fill_rate FROM {{p}}i_vend_state WHERE machine_id = 'M3' INTO @DATA(lv_rate). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '50.0' ) + act = CONV decfloat34( lv_rate ) ). + ENDMETHOD. + + METHOD status_empty. + SELECT SINGLE status FROM {{p}}i_vend_state WHERE machine_id = 'M2' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'EMPTY' act = lv_status ). + ENDMETHOD. + + METHOD status_low. + SELECT SINGLE status FROM {{p}}i_vend_state WHERE machine_id = 'M1' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'LOW' act = lv_status ). + ENDMETHOD. + + METHOD status_ok. + SELECT SINGLE status FROM {{p}}i_vend_state WHERE machine_id = 'M3' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'OK' act = lv_status ). + ENDMETHOD. + + METHOD status_at_limit. + SELECT SINGLE status FROM {{p}}i_vend_state WHERE machine_id = 'M5' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'OK' act = lv_status ). + ENDMETHOD. + + METHOD machine_without_slots. + SELECT COUNT(*) FROM {{p}}i_vend_state INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lv_rows ). + ENDMETHOD. + + METHOD location_upper. + SELECT SINGLE location FROM {{p}}i_vend_state WHERE machine_id = 'M1' INTO @DATA(lv_location). + cl_abap_unit_assert=>assert_equals( exp = 'HALL A' act = lv_location ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1080/mutation.json b/tasks_gen/train/G1080/mutation.json new file mode 100644 index 0000000..a22b419 --- /dev/null +++ b/tasks_gen/train/G1080/mutation.json @@ -0,0 +1,59 @@ +{ + "task": "G1080", + "mutants": [ + { + "object": "{{P}}I_VEND_STATE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "MACHINE_WITHOUT_SLOTS" + ] + }, + { + "object": "{{P}}I_VEND_STATE", + "mutant": "line 11: 1 -> 2 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "EMPTY_SLOTS" + ] + }, + { + "object": "{{P}}I_VEND_STATE", + "mutant": "line 14: sum( -> max( (agg)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "FILL_RATE_HALF" + ] + }, + { + "object": "{{P}}I_VEND_STATE", + "mutant": "line 17: = -> <> (eq)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "STATUS_AT_LIMIT", + "STATUS_EMPTY", + "STATUS_LOW", + "STATUS_OK" + ] + }, + { + "object": "{{P}}I_VEND_STATE", + "mutant": "line 19: < -> >= (rel)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "STATUS_AT_LIMIT", + "STATUS_LOW", + "STATUS_OK" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1080/reference/i_vend_state.ddls.asddls b/tasks_gen/train/G1080/reference/i_vend_state.ddls.asddls new file mode 100644 index 0000000..1a78b3f --- /dev/null +++ b/tasks_gen/train/G1080/reference/i_vend_state.ddls.asddls @@ -0,0 +1,25 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Vending machine state' +define view entity {{P}}I_VEND_STATE + as select from {{p}}vmach as machine + inner join {{p}}vslot as slot on slot.machine_id = machine.machine_id +{ + key machine.machine_id as machine_id, + upper( machine.location ) as location, + count( * ) as slot_count, + cast( sum( case when slot.fill_level = 0 + then 1 + else 0 + end ) as abap.int4 ) as empty_slots, + division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) as fill_rate, + case + when sum( slot.fill_level ) = 0 then 'EMPTY' + when division( sum( slot.fill_level ) * 100, + sum( slot.max_units ), 1 ) < 25 then 'LOW' + else 'OK' + end as status +} +group by + machine.machine_id, + machine.location diff --git a/tasks_gen/train/G1080/reference/t16_test.clas.abap b/tasks_gen/train/G1080/reference/t16_test.clas.abap new file mode 100644 index 0000000..ef95894 --- /dev/null +++ b/tasks_gen/train/G1080/reference/t16_test.clas.abap @@ -0,0 +1,56 @@ +CLASS {{p}}t16_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS low_machine FOR TESTING. + METHODS empty_machine FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_VEND_STATE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD low_machine. + DATA lt_mach TYPE STANDARD TABLE OF {{p}}vmach WITH EMPTY KEY. + DATA lt_slot TYPE STANDARD TABLE OF {{p}}vslot WITH EMPTY KEY. + lt_mach = VALUE #( ( machine_id = 'A' location = 'Hall' ) ). + lt_slot = VALUE #( ( machine_id = 'A' slot_no = 1 product = 'COLA' fill_level = 1 max_units = 4 ) + ( machine_id = 'A' slot_no = 2 product = 'TEA' fill_level = 0 max_units = 4 ) ). + go_env->insert_test_data( lt_mach ). + go_env->insert_test_data( lt_slot ). + SELECT SINGLE slot_count, empty_slots, fill_rate, status FROM {{p}}i_vend_state + WHERE machine_id = 'A' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-slot_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-empty_slots ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.5' ) + act = CONV decfloat34( ls_row-fill_rate ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOW' act = ls_row-status ). + ENDMETHOD. + + METHOD empty_machine. + DATA lt_mach TYPE STANDARD TABLE OF {{p}}vmach WITH EMPTY KEY. + DATA lt_slot TYPE STANDARD TABLE OF {{p}}vslot WITH EMPTY KEY. + lt_mach = VALUE #( ( machine_id = 'B' location = 'Gate' ) ). + lt_slot = VALUE #( ( machine_id = 'B' slot_no = 1 product = 'NUTS' fill_level = 0 max_units = 6 ) ). + go_env->insert_test_data( lt_mach ). + go_env->insert_test_data( lt_slot ). + SELECT SINGLE fill_rate, status FROM {{p}}i_vend_state + WHERE machine_id = 'B' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.0' ) + act = CONV decfloat34( ls_row-fill_rate ) ). + cl_abap_unit_assert=>assert_equals( exp = 'EMPTY' act = ls_row-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1080/seed/vmach.tabl.asabap b/tasks_gen/train/G1080/seed/vmach.tabl.asabap new file mode 100644 index 0000000..c127cc6 --- /dev/null +++ b/tasks_gen/train/G1080/seed/vmach.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Vending machine' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}vmach { + key client : abap.clnt not null; + key machine_id : abap.char(10) not null; + location : abap.char(20); +} diff --git a/tasks_gen/train/G1080/seed/vslot.tabl.asabap b/tasks_gen/train/G1080/seed/vslot.tabl.asabap new file mode 100644 index 0000000..5ab1195 --- /dev/null +++ b/tasks_gen/train/G1080/seed/vslot.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Vending machine slot' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}vslot { + key client : abap.clnt not null; + key machine_id : abap.char(10) not null; + key slot_no : abap.int4 not null; + product : abap.char(10); + fill_level : abap.int4; + max_units : abap.int4; +} diff --git a/tasks_gen/train/G1080/spec.md b/tasks_gen/train/G1080/spec.md new file mode 100644 index 0000000..cced365 --- /dev/null +++ b/tasks_gen/train/G1080/spec.md @@ -0,0 +1,44 @@ +# 1. Goal +Show the state of each vending machine. A dashboard and an OData service read the +state, so the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}VMACH (vending machine), package $TMP. Key: MACHINE_ID (CHAR 10). + Field: LOCATION (CHAR 20). +- Table {{P}}VSLOT (vending machine slot), package $TMP. + Key: MACHINE_ID (CHAR 10), SLOT_NO (INT4). + Fields: PRODUCT (CHAR 10), FILL_LEVEL (INT4, units that remain in the slot), + MAX_UNITS (INT4, capacity of the slot in units). + +# 4. Contract +- Create the CDS view entity {{P}}I_VEND_STATE in package $TMP. +- Elements, with these names and types: + MACHINE_ID (key, CHAR 10), LOCATION (CHAR 20), SLOT_COUNT (INT4), + EMPTY_SLOTS (INT4), FILL_RATE (DEC 5,1), STATUS (CHAR 5). +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each machine that has at least one slot. Do not show a + machine without slots. +2. SLOT_COUNT is the number of slots of the machine. +3. EMPTY_SLOTS is the number of slots of the machine with a fill level of zero. +4. FILL_RATE is the total fill level of the machine as a percentage of the total + capacity of the machine. FILL_RATE has one decimal place. Example: a total + fill level of 3 units and a total capacity of 20 units give 15.0. +5. LOCATION is the location of the machine in upper case letters. +6. STATUS is 'EMPTY' if the total fill level of the machine is zero. STATUS is + 'LOW' if the total fill level is not zero and FILL_RATE is less than 25.0. + STATUS is 'OK' in all other cases. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep methods below 40 statements. +- Out of scope: do not change {{P}}VMACH and {{P}}VSLOT. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1080/task.json b/tasks_gen/train/G1080/task.json new file mode 100644 index 0000000..ac40a34 --- /dev/null +++ b/tasks_gen/train/G1080/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1080", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}VMACH", + "file": "seed/vmach.tabl.asabap", + "description": "Vending machine" + }, + { + "type": "TABL", + "name": "{{P}}VSLOT", + "file": "seed/vslot.tabl.asabap", + "description": "Vending machine slot" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_VEND_STATE", + "fields": [ + "MACHINE_ID", + "LOCATION", + "SLOT_COUNT", + "EMPTY_SLOTS", + "FILL_RATE", + "STATUS" + ] + } + ], + "out_of_scope": [ + "{{P}}VMACH", + "{{P}}VSLOT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_VEND_STATE", + "file": "reference/i_vend_state.ddls.asddls", + "description": "Vending machine state" + }, + { + "type": "CLAS", + "name": "{{P}}T16_TEST", + "file": "reference/t16_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1081/faulty/m0_studio_summary.clas.abap b/tasks_gen/train/G1081/faulty/m0_studio_summary.clas.abap new file mode 100644 index 0000000..0c24cc7 --- /dev/null +++ b/tasks_gen/train/G1081/faulty/m0_studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL AND -fee <= 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee + -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count + 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/faulty/m1_studio_summary.clas.abap b/tasks_gen/train/G1081/faulty/m1_studio_summary.clas.abap new file mode 100644 index 0000000..4fcdb79 --- /dev/null +++ b/tasks_gen/train/G1081/faulty/m1_studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL OR -fee > 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee + -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count + 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/faulty/m2_studio_summary.clas.abap b/tasks_gen/train/G1081/faulty/m2_studio_summary.clas.abap new file mode 100644 index 0000000..c88e392 --- /dev/null +++ b/tasks_gen/train/G1081/faulty/m2_studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL OR -fee <= 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee - -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count + 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/faulty/m3_studio_summary.clas.abap b/tasks_gen/train/G1081/faulty/m3_studio_summary.clas.abap new file mode 100644 index 0000000..720efc7 --- /dev/null +++ b/tasks_gen/train/G1081/faulty/m3_studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL OR -fee <= 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee + -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count - 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/faulty/m4_studio_summary.clas.abap b/tasks_gen/train/G1081/faulty/m4_studio_summary.clas.abap new file mode 100644 index 0000000..00480e3 --- /dev/null +++ b/tasks_gen/train/G1081/faulty/m4_studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL OR -fee <= 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee + -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count + 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count - 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/generation.json b/tasks_gen/train/G1081/generation.json new file mode 100644 index 0000000..35fa0b3 --- /dev/null +++ b/tasks_gen/train/G1081/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1081", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 96.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1081/hidden/t30_hidden.clas.abap b/tasks_gen/train/G1081/hidden/t30_hidden.clas.abap new file mode 100644 index 0000000..e72e8b6 --- /dev/null +++ b/tasks_gen/train/G1081/hidden/t30_hidden.clas.abap @@ -0,0 +1,139 @@ +CLASS {{p}}t30_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + DATA mo_cut TYPE REF TO {{p}}studio_summary. + METHODS setup. + METHODS empty_input FOR TESTING. + METHODS single_entry FOR TESTING. + METHODS two_studios_same_type FOR TESTING. + METHODS ignores_zero_fee FOR TESTING. + METHODS ignores_negative_fee FOR TESTING. + METHODS ignores_empty_type FOR TESTING. + METHODS removes_duplicates FOR TESTING. + METHODS same_member_two_types FOR TESTING. + METHODS same_class_two_studios FOR TESTING. + METHODS sorted_by_class_type FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t30_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}studio_summary( ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD single_entry. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '12.50' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'YOGA' act = lt_result[ 1 ]-class_type ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-member_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '12.50' ) + act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD two_studios_same_type. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S002' class_id = 'C0000002' class_type = 'YOGA' + member_id = 'M0000002' fee = '15.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-member_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '25.00' ) + act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_zero_fee. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '0.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD ignores_negative_fee. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '-5.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD ignores_empty_type. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = '' + member_id = 'M0000001' fee = '10.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD removes_duplicates. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '99.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-member_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '10.00' ) + act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD same_member_two_types. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S001' class_id = 'C0000002' class_type = 'PILATES' + member_id = 'M0000001' fee = '12.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'PILATES' act = lt_result[ 1 ]-class_type ). + cl_abap_unit_assert=>assert_equals( exp = 'YOGA' act = lt_result[ 2 ]-class_type ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-member_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 2 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 2 ]-member_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '12.00' ) + act = lt_result[ 1 ]-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '10.00' ) + act = lt_result[ 2 ]-total_fee ). + ENDMETHOD. + + METHOD same_class_two_studios. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S002' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000002' fee = '10.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD sorted_by_class_type. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'ZUMBA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S001' class_id = 'C0000002' class_type = 'AEROBIC' + member_id = 'M0000002' fee = '10.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AEROBIC' act = lt_result[ 1 ]-class_type ). + cl_abap_unit_assert=>assert_equals( exp = 'ZUMBA' act = lt_result[ 2 ]-class_type ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/mutation.json b/tasks_gen/train/G1081/mutation.json new file mode 100644 index 0000000..f47dfe8 --- /dev/null +++ b/tasks_gen/train/G1081/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G1081", + "mutants": [ + { + "object": "{{P}}STUDIO_SUMMARY", + "mutant": "line 37: OR -> AND (logic)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "IGNORES_EMPTY_TYPE", + "IGNORES_NEGATIVE_FEE", + "IGNORES_ZERO_FEE" + ] + }, + { + "object": "{{P}}STUDIO_SUMMARY", + "mutant": "line 37: <= -> > (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "IGNORES_NEGATIVE_FEE", + "IGNORES_ZERO_FEE", + "REMOVES_DUPLICATES", + "SAME_CLASS_TWO_STUDIOS", + "SAME_MEMBER_TWO_TYPES", + "SINGLE_ENTRY", + "SORTED_BY_CLASS_TYPE", + "TWO_STUDIOS_SAME_TYPE" + ] + }, + { + "object": "{{P}}STUDIO_SUMMARY", + "mutant": "line 50: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "REMOVES_DUPLICATES", + "SAME_MEMBER_TWO_TYPES", + "SINGLE_ENTRY", + "TWO_STUDIOS_SAME_TYPE" + ] + }, + { + "object": "{{P}}STUDIO_SUMMARY", + "mutant": "line 53: + -> - (arith)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "REMOVES_DUPLICATES", + "SAME_CLASS_TWO_STUDIOS", + "SAME_MEMBER_TWO_TYPES", + "SINGLE_ENTRY", + "TWO_STUDIOS_SAME_TYPE" + ] + }, + { + "object": "{{P}}STUDIO_SUMMARY", + "mutant": "line 57: + -> - (arith)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "REMOVES_DUPLICATES", + "SAME_CLASS_TWO_STUDIOS", + "SAME_MEMBER_TWO_TYPES", + "SINGLE_ENTRY", + "TWO_STUDIOS_SAME_TYPE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1081/reference/studio_summary.clas.abap b/tasks_gen/train/G1081/reference/studio_summary.clas.abap new file mode 100644 index 0000000..b956ed0 --- /dev/null +++ b/tasks_gen/train/G1081/reference/studio_summary.clas.abap @@ -0,0 +1,64 @@ +CLASS {{p}}studio_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_attendance, + studio TYPE c LENGTH 4, + class_id TYPE c LENGTH 8, + class_type TYPE c LENGTH 10, + member_id TYPE c LENGTH 8, + fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_attendance, + tt_attendance TYPE STANDARD TABLE OF ty_attendance WITH EMPTY KEY, + BEGIN OF ty_summary, + class_type TYPE c LENGTH 10, + class_count TYPE i, + member_count TYPE i, + total_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_summary, + tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +ENDCLASS. + + +CLASS {{p}}studio_summary IMPLEMENTATION. + METHOD summarize. + DATA lt_seen TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id member_id. + DATA lt_class TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY studio class_id class_type. + DATA lt_member TYPE HASHED TABLE OF ty_attendance + WITH UNIQUE KEY class_type member_id. + DATA lt_groups TYPE HASHED TABLE OF ty_summary WITH UNIQUE KEY class_type. + + LOOP AT it_attendance ASSIGNING FIELD-SYMBOL(). + IF -class_type IS INITIAL OR -fee <= 0. + CONTINUE. + ENDIF. + INSERT INTO TABLE lt_seen. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + ASSIGN lt_groups[ class_type = -class_type ] + TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE ty_summary( class_type = -class_type ) + INTO TABLE lt_groups ASSIGNING . + ENDIF. + -total_fee = -total_fee + -fee. + INSERT INTO TABLE lt_class. + IF sy-subrc = 0. + -class_count = -class_count + 1. + ENDIF. + INSERT INTO TABLE lt_member. + IF sy-subrc = 0. + -member_count = -member_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_groups. + SORT rt_summary BY class_type. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/reference/studio_summary.testclasses.abap b/tasks_gen/train/G1081/reference/studio_summary.testclasses.abap new file mode 100644 index 0000000..a009ef0 --- /dev/null +++ b/tasks_gen/train/G1081/reference/studio_summary.testclasses.abap @@ -0,0 +1,71 @@ +CLASS ltc_studio_summary DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + DATA mo_cut TYPE REF TO {{p}}studio_summary. + METHODS setup. + METHODS single_entry FOR TESTING. + METHODS ignores_invalid_fee FOR TESTING. + METHODS removes_duplicates FOR TESTING. + METHODS groups_two_studios FOR TESTING. + METHODS sorts_by_class_type FOR TESTING. +ENDCLASS. + + +CLASS ltc_studio_summary IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}studio_summary( ). + ENDMETHOD. + + METHOD single_entry. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '12.50' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '12.50' ) + act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD ignores_invalid_fee. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '0.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD removes_duplicates. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '99.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_fee( '10.00' ) + act = lt_result[ 1 ]-total_fee ). + ENDMETHOD. + + METHOD groups_two_studios. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'YOGA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S002' class_id = 'C0000002' class_type = 'YOGA' + member_id = 'M0000002' fee = '15.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-class_count ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD sorts_by_class_type. + DATA lt_input TYPE {{p}}studio_summary=>tt_attendance. + lt_input = VALUE #( ( studio = 'S001' class_id = 'C0000001' class_type = 'ZUMBA' + member_id = 'M0000001' fee = '10.00' ) + ( studio = 'S001' class_id = 'C0000002' class_type = 'AEROBIC' + member_id = 'M0000002' fee = '10.00' ) ). + DATA(lt_result) = mo_cut->summarize( lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 'AEROBIC' act = lt_result[ 1 ]-class_type ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1081/spec.md b/tasks_gen/train/G1081/spec.md new file mode 100644 index 0000000..061e030 --- /dev/null +++ b/tasks_gen/train/G1081/spec.md @@ -0,0 +1,55 @@ +# 1. Goal +A fitness chain runs several studios. Each studio sends the attendance list of +its classes for one day. The head office needs one summary row for each class +type. The summary shows how many classes took place, how many members attended, +and the total fee. + +# 2. Open questions +None. + +# 3. Context +- All objects are in package $TMP. +- The class {{P}}STUDIO_SUMMARY does not exist yet. +- Each studio sends its own list. The caller appends the lists of all studios + into one table and calls the class once. + +# 4. Contract +- Create the class {{P}}STUDIO_SUMMARY in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class defines these public types: + - ty_attendance: studio (CHAR 4), class_id (CHAR 8), class_type (CHAR 10), + member_id (CHAR 8), fee (PACKED, length 8, 2 decimals). + - tt_attendance: a table of ty_attendance. + - ty_summary: class_type (CHAR 10), class_count (INT4), member_count (INT4), + total_fee (PACKED, length 8, 2 decimals). + - tt_summary: a table of ty_summary. +- The class has this public method: + summarize + IMPORTING it_attendance TYPE tt_attendance + RETURNING VALUE(rt_summary) TYPE tt_summary. +- Do not add other public methods. + +# 5. Business rules +1. Use only entries with a class_type that is not empty and a fee greater than + zero. Ignore all other entries. +2. If two of the remaining entries have the same studio, class_id and member_id, + keep only the first of these entries in the input order and ignore the later + entries. +3. Group the remaining entries by class_type. +4. class_count is the number of different combinations of studio and class_id + in the group. +5. member_count is the number of different member_id values in the group. +6. total_fee is the sum of the fee of all entries in the group. +7. Return one row for each class_type that has at least one entry. Sort the rows + by class_type 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: no database access, no ALV list, no report, no function module. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1081/task.json b/tasks_gen/train/G1081/task.json new file mode 100644 index 0000000..8d92a14 --- /dev/null +++ b/tasks_gen/train/G1081/task.json @@ -0,0 +1,42 @@ +{ + "id": "G1081", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}STUDIO_SUMMARY" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T30_HIDDEN", + "file": "hidden/t30_hidden.clas.abap", + "description": "T30 hidden tests for the studio class summary" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}STUDIO_SUMMARY", + "file": "reference/studio_summary.clas.abap", + "description": "Studio class summary", + "testclasses_file": "reference/studio_summary.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_nested_loops", + "keyed_table_access" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1082/faulty/m0_climb_pricer.clas.abap b/tasks_gen/train/G1082/faulty/m0_climb_pricer.clas.abap new file mode 100644 index 0000000..4b89d5b --- /dev/null +++ b/tasks_gen/train/G1082/faulty/m0_climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type = c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours OR is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_true. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge + c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member = abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/faulty/m1_climb_pricer.clas.abap b/tasks_gen/train/G1082/faulty/m1_climb_pricer.clas.abap new file mode 100644 index 0000000..066ca3b --- /dev/null +++ b/tasks_gen/train/G1082/faulty/m1_climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type <> c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours AND is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_true. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge + c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member = abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/faulty/m2_climb_pricer.clas.abap b/tasks_gen/train/G1082/faulty/m2_climb_pricer.clas.abap new file mode 100644 index 0000000..56dc8d7 --- /dev/null +++ b/tasks_gen/train/G1082/faulty/m2_climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type <> c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours OR is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_false. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge + c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member = abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/faulty/m3_climb_pricer.clas.abap b/tasks_gen/train/G1082/faulty/m3_climb_pricer.clas.abap new file mode 100644 index 0000000..8c39d71 --- /dev/null +++ b/tasks_gen/train/G1082/faulty/m3_climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type <> c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours OR is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_true. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge - c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member = abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/faulty/m4_climb_pricer.clas.abap b/tasks_gen/train/G1082/faulty/m4_climb_pricer.clas.abap new file mode 100644 index 0000000..c2eb58f --- /dev/null +++ b/tasks_gen/train/G1082/faulty/m4_climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type <> c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours OR is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_true. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge + c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member <> abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/generation.json b/tasks_gen/train/G1082/generation.json new file mode 100644 index 0000000..03821f7 --- /dev/null +++ b/tasks_gen/train/G1082/generation.json @@ -0,0 +1,37 @@ +{ + "id": "G1082", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G1042 (similarity spec 0.66, rules 0.63, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G1030 (similarity spec 0.76, rules 0.60, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1082/hidden/climb_hidden.clas.abap b/tasks_gen/train/G1082/hidden/climb_hidden.clas.abap new file mode 100644 index 0000000..6c46872 --- /dev/null +++ b/tasks_gen/train/G1082/hidden/climb_hidden.clas.abap @@ -0,0 +1,136 @@ +CLASS {{p}}climb_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}climb_pricer. + METHODS setup. + METHODS assert_amount + IMPORTING iv_expected TYPE {{p}}climb_pricer=>ty_amount + iv_actual TYPE {{p}}climb_pricer=>ty_amount + iv_text TYPE string. + METHODS adult_two_hours FOR TESTING. + METHODS youth_single_hour FOR TESTING. + METHODS family_rate FOR TESTING. + METHODS equipment_fee FOR TESTING. + METHODS group_fee FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS member_beats_loyalty FOR TESTING. + METHODS top_loyalty FOR TESTING. + METHODS mid_loyalty FOR TESTING. + METHODS unknown_entry FOR TESTING. + METHODS invalid_hours FOR TESTING. + METHODS invalid_climbers FOR TESTING. +ENDCLASS. + + +CLASS {{p}}climb_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}climb_pricer( ). + ENDMETHOD. + + METHOD assert_amount. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual msg = iv_text ). + ENDMETHOD. + + METHOD adult_two_hours. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 ) ). + assert_amount( iv_expected = '36.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-surcharge iv_text = 'surcharge' ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '36.00' iv_actual = ls_result-total iv_text = 'total' ). + cl_abap_unit_assert=>assert_initial( act = ls_result-message msg = 'message must be empty' ). + ENDMETHOD. + + METHOD youth_single_hour. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'Y' hours = 1 climbers = 1 ) ). + assert_amount( iv_expected = '6.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '6.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD family_rate. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'F' hours = 3 climbers = 4 ) ). + assert_amount( iv_expected = '84.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '84.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD equipment_fee. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 3 + equipment = abap_true ) ). + assert_amount( iv_expected = '54.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '12.00' iv_actual = ls_result-surcharge iv_text = 'surcharge' ). + assert_amount( iv_expected = '66.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD group_fee. + DATA(ls_small) = mo_cut->price( VALUE #( entry_type = 'A' hours = 1 climbers = 5 ) ). + assert_amount( iv_expected = '45.00' iv_actual = ls_small-base iv_text = 'small base' ). + assert_amount( iv_expected = '0.00' iv_actual = ls_small-surcharge iv_text = 'small surcharge' ). + + DATA(ls_group) = mo_cut->price( VALUE #( entry_type = 'A' hours = 1 climbers = 6 ) ). + assert_amount( iv_expected = '54.00' iv_actual = ls_group-base iv_text = 'group base' ). + assert_amount( iv_expected = '15.00' iv_actual = ls_group-surcharge iv_text = 'group surcharge' ). + assert_amount( iv_expected = '69.00' iv_actual = ls_group-total iv_text = 'group total' ). + ENDMETHOD. + + METHOD member_discount. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + member = abap_true ) ). + assert_amount( iv_expected = '36.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '5.40' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '30.60' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD member_beats_loyalty. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + member = abap_true visits = 10 ) ). + assert_amount( iv_expected = '5.40' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '30.60' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD top_loyalty. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + visits = 10 ) ). + assert_amount( iv_expected = '7.20' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '28.80' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD mid_loyalty. + DATA(ls_three) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + visits = 3 ) ). + assert_amount( iv_expected = '1.80' iv_actual = ls_three-discount iv_text = 'discount' ). + assert_amount( iv_expected = '34.20' iv_actual = ls_three-total iv_text = 'total' ). + + DATA(ls_two) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + visits = 2 ) ). + assert_amount( iv_expected = '0.00' iv_actual = ls_two-discount iv_text = 'no discount' ). + assert_amount( iv_expected = '36.00' iv_actual = ls_two-total iv_text = 'no discount total' ). + ENDMETHOD. + + METHOD unknown_entry. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'X' hours = 2 climbers = 2 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN ENTRY TYPE' act = ls_result-message ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '0.00' iv_actual = ls_result-total iv_text = 'total' ). + + DATA(ls_order) = mo_cut->price( VALUE #( entry_type = 'X' hours = 0 climbers = 0 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN ENTRY TYPE' act = ls_order-message ). + ENDMETHOD. + + METHOD invalid_hours. + DATA(ls_zero) = mo_cut->price( VALUE #( entry_type = 'A' hours = 0 climbers = 2 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID HOURS' act = ls_zero-message ). + + DATA(ls_many) = mo_cut->price( VALUE #( entry_type = 'A' hours = 7 climbers = 2 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID HOURS' act = ls_many-message ). + + DATA(ls_order) = mo_cut->price( VALUE #( entry_type = 'A' hours = 0 climbers = 0 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID HOURS' act = ls_order-message ). + ENDMETHOD. + + METHOD invalid_climbers. + DATA(ls_none) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 0 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID CLIMBERS' act = ls_none-message ). + + DATA(ls_many) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 11 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID CLIMBERS' act = ls_many-message ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/mutation.json b/tasks_gen/train/G1082/mutation.json new file mode 100644 index 0000000..b0c8bcf --- /dev/null +++ b/tasks_gen/train/G1082/mutation.json @@ -0,0 +1,79 @@ +{ + "task": "G1082", + "mutants": [ + { + "object": "{{P}}CLIMB_PRICER", + "mutant": "line 93: <> -> = (rel)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "ADULT_TWO_HOURS", + "EQUIPMENT_FEE", + "GROUP_FEE", + "INVALID_CLIMBERS", + "INVALID_HOURS", + "MEMBER_BEATS_LOYALTY", + "MEMBER_DISCOUNT", + "MID_LOYALTY", + "TOP_LOYALTY", + "UNKNOWN_ENTRY" + ] + }, + { + "object": "{{P}}CLIMB_PRICER", + "mutant": "line 97: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "INVALID_HOURS" + ] + }, + { + "object": "{{P}}CLIMB_PRICER", + "mutant": "line 120: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "ADULT_TWO_HOURS", + "EQUIPMENT_FEE", + "FAMILY_RATE", + "GROUP_FEE", + "MEMBER_BEATS_LOYALTY", + "MEMBER_DISCOUNT", + "MID_LOYALTY", + "TOP_LOYALTY", + "YOUTH_SINGLE_HOUR" + ] + }, + { + "object": "{{P}}CLIMB_PRICER", + "mutant": "line 124: + -> - (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "GROUP_FEE" + ] + }, + { + "object": "{{P}}CLIMB_PRICER", + "mutant": "line 129: = -> <> (eq)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "ADULT_TWO_HOURS", + "EQUIPMENT_FEE", + "FAMILY_RATE", + "GROUP_FEE", + "MEMBER_BEATS_LOYALTY", + "MEMBER_DISCOUNT", + "MID_LOYALTY", + "TOP_LOYALTY", + "YOUTH_SINGLE_HOUR" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1082/reference/climb_pricer.clas.abap b/tasks_gen/train/G1082/reference/climb_pricer.clas.abap new file mode 100644 index 0000000..66e9de7 --- /dev/null +++ b/tasks_gen/train/G1082/reference/climb_pricer.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_entry, + adult TYPE ty_entry VALUE 'A', + youth TYPE ty_entry VALUE 'Y', + family TYPE ty_entry VALUE 'F', + END OF c_entry. + CONSTANTS: + BEGIN OF c_message, + unknown_entry TYPE string VALUE 'UNKNOWN ENTRY TYPE', + invalid_hours TYPE string VALUE 'INVALID HOURS', + invalid_climbers TYPE string VALUE 'INVALID CLIMBERS', + END OF c_message. + CONSTANTS: + c_min_hours TYPE i VALUE 1, + c_max_hours TYPE i VALUE 6, + c_min_climbers TYPE i VALUE 1, + c_max_climbers TYPE i VALUE 10, + c_group_size TYPE i VALUE 6, + c_group_fee TYPE ty_amount VALUE '15.00', + c_equipment_fee TYPE ty_amount VALUE '4.00', + c_top_visits TYPE i VALUE 10, + c_mid_visits TYPE i VALUE 3, + c_member_percent TYPE i VALUE 15, + c_top_percent TYPE i VALUE 20, + c_mid_percent TYPE i VALUE 5, + c_percent TYPE i VALUE 100, + c_rate_adult TYPE ty_amount VALUE '9.00', + c_rate_youth TYPE ty_amount VALUE '6.00', + c_rate_family TYPE ty_amount VALUE '7.00'. + + METHODS check_session + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_message) TYPE string. + METHODS hourly_rate + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS base_price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_base) TYPE ty_amount. + METHODS extra_charge + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS rebate + IMPORTING iv_gross TYPE ty_amount + is_session TYPE ty_session + RETURNING VALUE(rv_discount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA(lv_message) = check_session( is_session ). + IF lv_message IS NOT INITIAL. + rs_result-message = lv_message. + RETURN. + ENDIF. + + rs_result-base = base_price( is_session ). + rs_result-surcharge = extra_charge( is_session ). + DATA lv_gross TYPE ty_amount. + lv_gross = rs_result-base + rs_result-surcharge. + rs_result-discount = rebate( iv_gross = lv_gross is_session = is_session ). + rs_result-total = lv_gross - rs_result-discount. + ENDMETHOD. + + METHOD check_session. + IF is_session-entry_type <> c_entry-adult + AND is_session-entry_type <> c_entry-youth + AND is_session-entry_type <> c_entry-family. + rv_message = c_message-unknown_entry. + ELSEIF is_session-hours < c_min_hours OR is_session-hours > c_max_hours. + rv_message = c_message-invalid_hours. + ELSEIF is_session-climbers < c_min_climbers OR is_session-climbers > c_max_climbers. + rv_message = c_message-invalid_climbers. + ENDIF. + ENDMETHOD. + + METHOD hourly_rate. + CASE is_session-entry_type. + WHEN c_entry-adult. + rv_rate = c_rate_adult. + WHEN c_entry-youth. + rv_rate = c_rate_youth. + WHEN OTHERS. + rv_rate = c_rate_family. + ENDCASE. + ENDMETHOD. + + METHOD base_price. + rv_base = is_session-hours * hourly_rate( is_session ) * is_session-climbers. + ENDMETHOD. + + METHOD extra_charge. + IF is_session-equipment = abap_true. + rv_surcharge = is_session-climbers * c_equipment_fee. + ENDIF. + IF is_session-climbers >= c_group_size. + rv_surcharge = rv_surcharge + c_group_fee. + ENDIF. + ENDMETHOD. + + METHOD rebate. + IF is_session-member = abap_true. + rv_discount = iv_gross * c_member_percent / c_percent. + ELSEIF is_session-visits >= c_top_visits. + rv_discount = iv_gross * c_top_percent / c_percent. + ELSEIF is_session-visits >= c_mid_visits. + rv_discount = iv_gross * c_mid_percent / c_percent. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/reference/climb_pricer.testclasses.abap b/tasks_gen/train/G1082/reference/climb_pricer.testclasses.abap new file mode 100644 index 0000000..aecc0a7 --- /dev/null +++ b/tasks_gen/train/G1082/reference/climb_pricer.testclasses.abap @@ -0,0 +1,88 @@ +CLASS ltc_climb_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}climb_pricer. + METHODS setup. + METHODS assert_amount + IMPORTING iv_expected TYPE {{p}}climb_pricer=>ty_amount + iv_actual TYPE {{p}}climb_pricer=>ty_amount + iv_text TYPE string. + METHODS adult_booking FOR TESTING. + METHODS youth_booking FOR TESTING. + METHODS family_booking FOR TESTING. + METHODS equipment_surcharge FOR TESTING. + METHODS group_surcharge FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS loyalty_discount FOR TESTING. + METHODS unknown_entry FOR TESTING. + METHODS invalid_hours FOR TESTING. + METHODS invalid_climbers FOR TESTING. +ENDCLASS. + + +CLASS ltc_climb_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}climb_pricer( ). + ENDMETHOD. + + METHOD assert_amount. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual msg = iv_text ). + ENDMETHOD. + + METHOD adult_booking. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 ) ). + assert_amount( iv_expected = '36.00' iv_actual = ls_result-base iv_text = 'base' ). + assert_amount( iv_expected = '36.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD youth_booking. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'Y' hours = 1 climbers = 1 ) ). + assert_amount( iv_expected = '6.00' iv_actual = ls_result-base iv_text = 'base' ). + ENDMETHOD. + + METHOD family_booking. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'F' hours = 3 climbers = 4 ) ). + assert_amount( iv_expected = '84.00' iv_actual = ls_result-base iv_text = 'base' ). + ENDMETHOD. + + METHOD equipment_surcharge. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 3 + equipment = abap_true ) ). + assert_amount( iv_expected = '12.00' iv_actual = ls_result-surcharge iv_text = 'surcharge' ). + assert_amount( iv_expected = '66.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD group_surcharge. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 1 climbers = 6 ) ). + assert_amount( iv_expected = '15.00' iv_actual = ls_result-surcharge iv_text = 'surcharge' ). + assert_amount( iv_expected = '69.00' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD member_discount. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + member = abap_true ) ). + assert_amount( iv_expected = '5.40' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '30.60' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD loyalty_discount. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 2 + visits = 10 ) ). + assert_amount( iv_expected = '7.20' iv_actual = ls_result-discount iv_text = 'discount' ). + assert_amount( iv_expected = '28.80' iv_actual = ls_result-total iv_text = 'total' ). + ENDMETHOD. + + METHOD unknown_entry. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'X' hours = 2 climbers = 2 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN ENTRY TYPE' act = ls_result-message ). + ENDMETHOD. + + METHOD invalid_hours. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 7 climbers = 2 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID HOURS' act = ls_result-message ). + ENDMETHOD. + + METHOD invalid_climbers. + DATA(ls_result) = mo_cut->price( VALUE #( entry_type = 'A' hours = 2 climbers = 11 ) ). + cl_abap_unit_assert=>assert_equals( exp = 'INVALID CLIMBERS' act = ls_result-message ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/seed/climb_pricer.clas.abap b/tasks_gen/train/G1082/seed/climb_pricer.clas.abap new file mode 100644 index 0000000..c53f6d6 --- /dev/null +++ b/tasks_gen/train/G1082/seed/climb_pricer.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}climb_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_entry TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_session, + entry_type TYPE ty_entry, + hours TYPE i, + climbers TYPE i, + equipment TYPE abap_bool, + member TYPE abap_bool, + visits TYPE i, + END OF ty_session. + TYPES: + BEGIN OF ty_result, + base TYPE ty_amount, + surcharge TYPE ty_amount, + discount TYPE ty_amount, + total TYPE ty_amount, + message TYPE string, + END OF ty_result. + METHODS price + IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. +ENDCLASS. + + +CLASS {{p}}climb_pricer IMPLEMENTATION. + METHOD price. + DATA: lv_rate TYPE ty_amount, + lv_gross TYPE ty_amount. + + CLEAR rs_result. + + IF is_session-entry_type = 'A' OR is_session-entry_type = 'Y' OR is_session-entry_type = 'F'. + IF is_session-hours GE 1 AND is_session-hours LE 6. + IF is_session-climbers GE 1 AND is_session-climbers LE 10. + IF is_session-entry_type = 'A'. + lv_rate = '9.00'. + ELSEIF is_session-entry_type = 'Y'. + lv_rate = '6.00'. + ELSE. + lv_rate = '7.00'. + ENDIF. + rs_result-base = is_session-hours * lv_rate * is_session-climbers. + IF is_session-equipment = 'X'. + rs_result-surcharge = is_session-climbers * '4.00'. + ENDIF. + IF is_session-climbers GE 6. + rs_result-surcharge = rs_result-surcharge + '15.00'. + ENDIF. + lv_gross = rs_result-base + rs_result-surcharge. + IF is_session-member = 'X'. + rs_result-discount = lv_gross * 15 / 100. + ELSEIF is_session-visits GE 10. + rs_result-discount = lv_gross * 20 / 100. + ELSEIF is_session-visits GE 3. + rs_result-discount = lv_gross * 5 / 100. + ENDIF. + rs_result-total = lv_gross - rs_result-discount. + ELSE. + rs_result-message = 'INVALID CLIMBERS'. + ENDIF. + ELSE. + rs_result-message = 'INVALID HOURS'. + ENDIF. + ELSE. + rs_result-message = 'UNKNOWN ENTRY TYPE'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1082/spec.md b/tasks_gen/train/G1082/spec.md new file mode 100644 index 0000000..1eaeae2 --- /dev/null +++ b/tasks_gen/train/G1082/spec.md @@ -0,0 +1,53 @@ +# 1. Goal +Refactor the class {{P}}CLIMB_PRICER in package $TMP. The class calculates the +price of a climbing session at a climbing centre. The booking program of the +centre calls the class for each session. The class is correct today, but the +code is hard to read and hard to change. Make the code clean and easy to +change. The behavior must stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}CLIMB_PRICER exists in package $TMP. It is active. +- The booking program calls the method PRICE and then reads the components of + the result structure. +- No user reports a wrong price. All prices are correct. +- The implementation is one long method with deep nesting and many literal + values. A change to one rate is risky. + +# 4. Contract +- Refactor the class {{P}}CLIMB_PRICER in package $TMP. The name stays the + same. +- The class is public and final. It has a public constructor without + parameters. +- The public interface must not change. It is: + - TYPES ty_entry TYPE c LENGTH 1. + - TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + - TYPES ty_session with the components entry_type TYPE ty_entry, + hours TYPE i, climbers TYPE i, equipment TYPE abap_bool, + member TYPE abap_bool and visits TYPE i. + - TYPES ty_result with the components base TYPE ty_amount, + surcharge TYPE ty_amount, discount TYPE ty_amount, total TYPE ty_amount + and message TYPE string. + - METHODS price IMPORTING is_session TYPE ty_session + RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods or public types. + +# 5. Business rules +The legacy code contains all the rules. Read the code of {{P}}CLIMB_PRICER +before you change it. Each input must give the same result as the legacy code. +Keep each limit, each rate, each surcharge, each discount and each message +text. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No magic numbers. No comment that restates the code. +- Out of scope: do not change the public interface of {{P}}CLIMB_PRICER. Do not + create other objects. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1082/task.json b/tasks_gen/train/G1082/task.json new file mode 100644 index 0000000..2bc27fd --- /dev/null +++ b/tasks_gen/train/G1082/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1082", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}CLIMB_PRICER", + "file": "seed/climb_pricer.clas.abap", + "description": "Legacy class that calculates the price of a climbing session" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CLIMB_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}CLIMB_HIDDEN", + "file": "hidden/climb_hidden.clas.abap", + "description": "Hidden tests that check the unchanged behavior of the refactored climbing session pricer" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CLIMB_PRICER", + "file": "reference/climb_pricer.clas.abap", + "description": "Refactored climbing session price class", + "testclasses_file": "reference/climb_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1083/faulty/m0_glaz_calc.clas.abap b/tasks_gen/train/G1083/faulty/m0_glaz_calc.clas.abap new file mode 100644 index 0000000..12fac34 --- /dev/null +++ b/tasks_gen/train/G1083/faulty/m0_glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 AND iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_true. + lv_fee = lv_fee * c_urgent_factor. + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/faulty/m1_glaz_calc.clas.abap b/tasks_gen/train/G1083/faulty/m1_glaz_calc.clas.abap new file mode 100644 index 0000000..452b6e2 --- /dev/null +++ b/tasks_gen/train/G1083/faulty/m1_glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 OR iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_false. + lv_fee = lv_fee * c_urgent_factor. + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/faulty/m2_glaz_calc.clas.abap b/tasks_gen/train/G1083/faulty/m2_glaz_calc.clas.abap new file mode 100644 index 0000000..f8a95e5 --- /dev/null +++ b/tasks_gen/train/G1083/faulty/m2_glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 OR iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_true. + lv_fee = lv_fee / c_urgent_factor. + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/faulty/m3_glaz_calc.clas.abap b/tasks_gen/train/G1083/faulty/m3_glaz_calc.clas.abap new file mode 100644 index 0000000..654566c --- /dev/null +++ b/tasks_gen/train/G1083/faulty/m3_glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 OR iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_true. + lv_fee = lv_fee * c_urgent_factor. + ENDIF. + IF lv_fee <= c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/faulty/m4_glaz_calc.clas.abap b/tasks_gen/train/G1083/faulty/m4_glaz_calc.clas.abap new file mode 100644 index 0000000..356e780 --- /dev/null +++ b/tasks_gen/train/G1083/faulty/m4_glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 OR iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_true. + lv_fee = lv_fee * c_urgent_factor. + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 3 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/generation.json b/tasks_gen/train/G1083/generation.json new file mode 100644 index 0000000..152ead9 --- /dev/null +++ b/tasks_gen/train/G1083/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1083", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1083/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1083/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..82bbed0 --- /dev/null +++ b/tasks_gen/train/G1083/hidden/t31_hidden.clas.abap @@ -0,0 +1,129 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_width TYPE i + iv_height TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool DEFAULT abap_false + EXPORTING + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS simple_pane_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS safety_pane_fee FOR TESTING. + METHODS insulated_pane_fee FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS urgent_surcharge FOR TESTING. + METHODS urgent_after_minimum FOR TESTING. + METHODS maximum_fee FOR TESTING. + METHODS invalid_size_rejected FOR TESTING. + METHODS invalid_glass_rejected FOR TESTING. + METHODS class_calculates_fee FOR TESTING. + METHODS class_rejects_bad_size FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD call. + CALL FUNCTION '{{P}}GLAZ_FEE' + EXPORTING + iv_width_mm = iv_width + iv_height_mm = iv_height + iv_glass = iv_glass + iv_urgent = iv_urgent + IMPORTING + ev_fee = ev_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD simple_pane_fee. + call( EXPORTING iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee. + call( EXPORTING iv_width = 500 iv_height = 400 iv_glass = `SIMPLE` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD safety_pane_fee. + call( EXPORTING iv_width = 1200 iv_height = 800 iv_glass = `SAFETY` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '86.40' ) act = lv_fee ). + ENDMETHOD. + + METHOD insulated_pane_fee. + call( EXPORTING iv_width = 2000 iv_height = 1500 iv_glass = `INSULATED` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '420.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD rounding_half_up. + call( EXPORTING iv_width = 1000 iv_height = 1001 iv_glass = `SIMPLE` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.05' ) act = lv_fee ). + ENDMETHOD. + + METHOD urgent_surcharge. + call( EXPORTING iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` iv_urgent = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '67.50' ) act = lv_fee ). + ENDMETHOD. + + METHOD urgent_after_minimum. + call( EXPORTING iv_width = 500 iv_height = 400 iv_glass = `SIMPLE` iv_urgent = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '52.50' ) act = lv_fee ). + ENDMETHOD. + + METHOD maximum_fee. + call( EXPORTING iv_width = 2000 iv_height = 1500 iv_glass = `INSULATED` iv_urgent = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '480.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_size_rejected. + call( EXPORTING iv_width = 0 iv_height = 1000 iv_glass = `SIMPLE` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + call( EXPORTING iv_width = 1000 iv_height = -5 iv_glass = `SIMPLE` + IMPORTING ev_subrc = DATA(lv_rc2) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc2 ). + ENDMETHOD. + + METHOD invalid_glass_rejected. + call( EXPORTING iv_width = 1000 iv_height = 1000 iv_glass = `TINTED` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD class_calculates_fee. + DATA(lo_calc) = NEW {{p}}glaz_calc( ). + TRY. + DATA(lv_fee) = lo_calc->calculate( iv_width_mm = 1200 iv_height_mm = 800 + iv_glass = `SAFETY` iv_urgent = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '86.40' ) act = lv_fee ). + CATCH cx_abap_invalid_value. + cl_abap_unit_assert=>fail( msg = `valid input was rejected` ). + ENDTRY. + ENDMETHOD. + + METHOD class_rejects_bad_size. + DATA(lo_calc) = NEW {{p}}glaz_calc( ). + TRY. + lo_calc->calculate( iv_width_mm = 1000 iv_height_mm = 0 + iv_glass = `SIMPLE` iv_urgent = abap_false ). + cl_abap_unit_assert=>fail( msg = `invalid size was accepted` ). + CATCH cx_abap_invalid_value. + cl_abap_unit_assert=>assert_true( abap_true ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/mutation.json b/tasks_gen/train/G1083/mutation.json new file mode 100644 index 0000000..6617cc7 --- /dev/null +++ b/tasks_gen/train/G1083/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1083", + "mutants": [ + { + "object": "{{P}}GLAZ_CALC", + "mutant": "line 23: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "CLASS_REJECTS_BAD_SIZE", + "INVALID_SIZE_REJECTED" + ] + }, + { + "object": "{{P}}GLAZ_CALC", + "mutant": "line 45: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "CLASS_CALCULATES_FEE", + "INSULATED_PANE_FEE", + "MAXIMUM_FEE", + "MINIMUM_FEE", + "ROUNDING_HALF_UP", + "SAFETY_PANE_FEE", + "SIMPLE_PANE_FEE", + "URGENT_AFTER_MINIMUM", + "URGENT_SURCHARGE" + ] + }, + { + "object": "{{P}}GLAZ_CALC", + "mutant": "line 46: * -> / (arith)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "MAXIMUM_FEE", + "URGENT_AFTER_MINIMUM", + "URGENT_SURCHARGE" + ] + }, + { + "object": "{{P}}GLAZ_CALC", + "mutant": "line 48: > -> <= (rel)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "CLASS_CALCULATES_FEE", + "INSULATED_PANE_FEE", + "MAXIMUM_FEE", + "MINIMUM_FEE", + "ROUNDING_HALF_UP", + "SAFETY_PANE_FEE", + "SIMPLE_PANE_FEE", + "URGENT_AFTER_MINIMUM", + "URGENT_SURCHARGE" + ] + }, + { + "object": "{{P}}GLAZ_CALC", + "mutant": "line 52: 2 -> 3 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "ROUNDING_HALF_UP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1083/reference/glaz_calc.clas.abap b/tasks_gen/train/G1083/reference/glaz_calc.clas.abap new file mode 100644 index 0000000..7fa7bf1 --- /dev/null +++ b/tasks_gen/train/G1083/reference/glaz_calc.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}glaz_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34 + RAISING + cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_area_divisor TYPE decfloat34 VALUE '1000000'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_max_fee TYPE decfloat34 VALUE '480.00'. + CONSTANTS c_urgent_factor TYPE decfloat34 VALUE '1.5'. +ENDCLASS. + + +CLASS {{p}}glaz_calc IMPLEMENTATION. + METHOD calculate. + IF iv_width_mm <= 0 OR iv_height_mm <= 0. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_glass. + WHEN 'SIMPLE'. + lv_rate = 45. + WHEN 'SAFETY'. + lv_rate = 90. + WHEN 'INSULATED'. + lv_rate = 140. + WHEN OTHERS. + RAISE EXCEPTION TYPE cx_abap_invalid_value. + ENDCASE. + + DATA(lv_area) = CONV decfloat34( iv_width_mm ) * iv_height_mm / c_area_divisor. + DATA(lv_fee) = lv_area * lv_rate. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + IF iv_urgent = abap_true. + lv_fee = lv_fee * c_urgent_factor. + ENDIF. + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + + rv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/reference/glaz_calc.clas.testclasses.abap b/tasks_gen/train/G1083/reference/glaz_calc.clas.testclasses.abap new file mode 100644 index 0000000..6fa02d2 --- /dev/null +++ b/tasks_gen/train/G1083/reference/glaz_calc.clas.testclasses.abap @@ -0,0 +1,73 @@ +CLASS ltc_glaz_calc DEFINITION FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_width TYPE i + iv_height TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS simple_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS urgent_fee FOR TESTING. + METHODS invalid_size FOR TESTING. + METHODS invalid_glass FOR TESTING. +ENDCLASS. + + +CLASS ltc_glaz_calc IMPLEMENTATION. + METHOD fee_for. + DATA(lo_calc) = NEW {{p}}glaz_calc( ). + TRY. + rv_fee = lo_calc->calculate( + iv_width_mm = iv_width + iv_height_mm = iv_height + iv_glass = iv_glass + iv_urgent = iv_urgent ). + CATCH cx_abap_invalid_value. + cl_abap_unit_assert=>fail( msg = `unexpected invalid value` ). + ENDTRY. + ENDMETHOD. + + METHOD simple_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '45.00' ) + act = fee_for( iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` iv_urgent = abap_false ) ). + ENDMETHOD. + + METHOD minimum_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '35.00' ) + act = fee_for( iv_width = 500 iv_height = 400 iv_glass = `SIMPLE` iv_urgent = abap_false ) ). + ENDMETHOD. + + METHOD urgent_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '67.50' ) + act = fee_for( iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` iv_urgent = abap_true ) ). + ENDMETHOD. + + METHOD invalid_size. + DATA(lo_calc) = NEW {{p}}glaz_calc( ). + TRY. + lo_calc->calculate( iv_width_mm = 0 iv_height_mm = 100 + iv_glass = `SIMPLE` iv_urgent = abap_false ). + cl_abap_unit_assert=>fail( msg = `invalid size was accepted` ). + CATCH cx_abap_invalid_value. + cl_abap_unit_assert=>assert_true( abap_true ). + ENDTRY. + ENDMETHOD. + + METHOD invalid_glass. + DATA(lo_calc) = NEW {{p}}glaz_calc( ). + TRY. + lo_calc->calculate( iv_width_mm = 100 iv_height_mm = 100 + iv_glass = `TINTED` iv_urgent = abap_false ). + cl_abap_unit_assert=>fail( msg = `invalid glass type was accepted` ). + CATCH cx_abap_invalid_value. + cl_abap_unit_assert=>assert_true( abap_true ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/reference/glaz_fee.func.abap b/tasks_gen/train/G1083/reference/glaz_fee.func.abap new file mode 100644 index 0000000..68d9ec3 --- /dev/null +++ b/tasks_gen/train/G1083/reference/glaz_fee.func.abap @@ -0,0 +1,25 @@ +FUNCTION {{p}}glaz_fee + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_input. + + DATA lo_calc TYPE REF TO {{p}}glaz_calc. + lo_calc = NEW #( ). + + TRY. + ev_fee = lo_calc->calculate( + iv_width_mm = iv_width_mm + iv_height_mm = iv_height_mm + iv_glass = iv_glass + iv_urgent = iv_urgent ). + CATCH cx_abap_invalid_value. + RAISE invalid_input. + ENDTRY. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1083/reference/t31_test.clas.abap b/tasks_gen/train/G1083/reference/t31_test.clas.abap new file mode 100644 index 0000000..0cac300 --- /dev/null +++ b/tasks_gen/train/G1083/reference/t31_test.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_width TYPE i + iv_height TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool DEFAULT abap_false + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS simple_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS urgent_fee FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}GLAZ_FEE' + EXPORTING + iv_width_mm = iv_width + iv_height_mm = iv_height + iv_glass = iv_glass + iv_urgent = iv_urgent + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD simple_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '45.00' ) + act = fee_for( iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` ) ). + ENDMETHOD. + + METHOD minimum_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '35.00' ) + act = fee_for( iv_width = 400 iv_height = 400 iv_glass = `SIMPLE` ) ). + ENDMETHOD. + + METHOD urgent_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '67.50' ) + act = fee_for( iv_width = 1000 iv_height = 1000 iv_glass = `SIMPLE` iv_urgent = abap_true ) ). + ENDMETHOD. + + METHOD invalid_input. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}GLAZ_FEE' + EXPORTING + iv_width_mm = 0 + iv_height_mm = 1000 + iv_glass = `SIMPLE` + iv_urgent = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1083/seed/glaz_sample.func.abap b/tasks_gen/train/G1083/seed/glaz_sample.func.abap new file mode 100644 index 0000000..4e59b4d --- /dev/null +++ b/tasks_gen/train/G1083/seed/glaz_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}glaz_sample + IMPORTING + iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_input. + + ev_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1083/spec.md b/tasks_gen/train/G1083/spec.md new file mode 100644 index 0000000..a7a1454 --- /dev/null +++ b/tasks_gen/train/G1083/spec.md @@ -0,0 +1,74 @@ +# 1. Goal + +A glazier workshop replaces window panes. For each pane of a job, the quotation +program calls one function module. The function module returns the fee of the +pane. Other programs of the workshop reuse the same calculation, so the +calculation must also be available as a method of a new class. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_GLAZ is in package $TMP. +- Function module {{P}}GLAZ_SAMPLE is in this function group. It is the sample + module. All pane fee modules use the interface of the sample module. +- The quotation program calls {{P}}GLAZ_FEE for each pane of a job. + +# 4. Contract + +- Create the function module {{P}}GLAZ_FEE in function group {{P}}FG_GLAZ. + Use exactly the interface of {{P}}GLAZ_SAMPLE: the same parameter names, the + same parameter kinds, the same types and the same exceptions. +- Create the class {{P}}GLAZ_CALC in package $TMP. The class is public and + final. The class has a public constructor without parameters. The class has + this public method: + + METHOD calculate + IMPORTING iv_width_mm TYPE i + iv_height_mm TYPE i + iv_glass TYPE string + iv_urgent TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + +- For the same input, {{P}}GLAZ_FEE returns the same fee as the method + calculate. + +# 5. Business rules + +The input of the calculation is: +- iv_width_mm: the width of the pane in millimetres. +- iv_height_mm: the height of the pane in millimetres. +- iv_glass: the code of the glass type. +- iv_urgent: the urgent flag. + +1. The width and the height must be greater than 0. If not, the input is + invalid. +2. The glass type code must be SIMPLE, SAFETY or INSULATED. If not, the input + is invalid. +3. The area of the pane in square metres is the width multiplied by the height + and divided by 1 000 000. +4. The price of one square metre is 45.00 for SIMPLE, 90.00 for SAFETY and + 140.00 for INSULATED. +5. The fee is the area multiplied by the price of one square metre. +6. If the fee is less than 35.00, the fee is 35.00. +7. If iv_urgent is true, the fee is multiplied by 1.5. +8. If the fee is more than 480.00, the fee is 480.00. +9. Round the fee half up to 2 decimals. This is the last step. +10. For invalid input, the method calculate raises cx_abap_invalid_value and + {{P}}GLAZ_FEE raises the exception invalid_input. + +# 6. Constraints + +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. A method has fewer than 40 statements. +- Out of scope: do not change {{P}}GLAZ_SAMPLE. + +# 7. Acceptance + +- {{P}}GLAZ_FEE and {{P}}GLAZ_CALC are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests: local test classes for {{P}}GLAZ_CALC and a global + test class for {{P}}GLAZ_FEE. diff --git a/tasks_gen/train/G1083/task.json b/tasks_gen/train/G1083/task.json new file mode 100644 index 0000000..44e247f --- /dev/null +++ b/tasks_gen/train/G1083/task.json @@ -0,0 +1,96 @@ +{ + "id": "G1083", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_GLAZ", + "description": "Pane fee modules" + }, + { + "type": "FUNC", + "name": "{{P}}GLAZ_SAMPLE", + "functionGroup": "{{P}}FG_GLAZ", + "file": "seed/glaz_sample.func.abap", + "description": "Sample pane module with the interface that all pane fee modules use" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}GLAZ_FEE", + "functionGroup": "{{P}}FG_GLAZ", + "params": [ + { + "name": "iv_width_mm", + "type": "i" + }, + { + "name": "iv_height_mm", + "type": "i" + }, + { + "name": "iv_glass", + "type": "string" + }, + { + "name": "iv_urgent", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + }, + { + "type": "CLAS", + "name": "{{P}}GLAZ_CALC", + "description": "Pane fee calculation with the public method calculate" + } + ], + "out_of_scope": [ + "{{P}}GLAZ_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}GLAZ_CALC", + "file": "reference/glaz_calc.clas.abap", + "testclasses_file": "reference/glaz_calc.clas.testclasses.abap", + "description": "Pane fee calculation" + }, + { + "type": "FUNC", + "name": "{{P}}GLAZ_FEE", + "functionGroup": "{{P}}FG_GLAZ", + "file": "reference/glaz_fee.func.abap", + "description": "Pane fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T31_TEST", + "file": "reference/t31_test.clas.abap", + "description": "T31 own tests for the function module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1084/faulty/m0_seed_lot_checker.clas.abap b/tasks_gen/train/G1084/faulty/m0_seed_lot_checker.clas.abap new file mode 100644 index 0000000..050a2c0 --- /dev/null +++ b/tasks_gen/train/G1084/faulty/m0_seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AND lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days < 0 OR lv_age_in_days > c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture > c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/faulty/m1_seed_lot_checker.clas.abap b/tasks_gen/train/G1084/faulty/m1_seed_lot_checker.clas.abap new file mode 100644 index 0000000..accb382 --- /dev/null +++ b/tasks_gen/train/G1084/faulty/m1_seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' OR lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days >= 0 OR lv_age_in_days > c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture > c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/faulty/m2_seed_lot_checker.clas.abap b/tasks_gen/train/G1084/faulty/m2_seed_lot_checker.clas.abap new file mode 100644 index 0000000..fa41483 --- /dev/null +++ b/tasks_gen/train/G1084/faulty/m2_seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' OR lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days < 0 AND lv_age_in_days > c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture > c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/faulty/m3_seed_lot_checker.clas.abap b/tasks_gen/train/G1084/faulty/m3_seed_lot_checker.clas.abap new file mode 100644 index 0000000..e308f4d --- /dev/null +++ b/tasks_gen/train/G1084/faulty/m3_seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' OR lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days < 0 OR lv_age_in_days <= c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture > c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/faulty/m4_seed_lot_checker.clas.abap b/tasks_gen/train/G1084/faulty/m4_seed_lot_checker.clas.abap new file mode 100644 index 0000000..067e39c --- /dev/null +++ b/tasks_gen/train/G1084/faulty/m4_seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' OR lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days < 0 OR lv_age_in_days > c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture <= c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/generation.json b/tasks_gen/train/G1084/generation.json new file mode 100644 index 0000000..78102e9 --- /dev/null +++ b/tasks_gen/train/G1084/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1084", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1084/hidden/seed_lot_hidden.clas.abap b/tasks_gen/train/G1084/hidden/seed_lot_hidden.clas.abap new file mode 100644 index 0000000..42ce8ef --- /dev/null +++ b/tasks_gen/train/G1084/hidden/seed_lot_hidden.clas.abap @@ -0,0 +1,210 @@ +CLASS {{p}}seed_lot_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS c_check_date TYPE d VALUE '20250601'. + DATA mo_cut TYPE REF TO {{p}}seed_lot_checker. + METHODS setup. + METHODS valid_lot + RETURNING VALUE(rs_lot) TYPE {{p}}seed_lot_checker=>ty_lot. + METHODS releases_valid_lot FOR TESTING. + METHODS rejects_bad_lot_no FOR TESTING. + METHODS rejects_lower_case_lot_no FOR TESTING. + METHODS rejects_initial_harvest FOR TESTING. + METHODS rejects_future_harvest FOR TESTING. + METHODS rejects_old_harvest FOR TESTING. + METHODS rejects_low_germination FOR TESTING. + METHODS rejects_high_moisture FOR TESTING. + METHODS accepts_limit_values FOR TESTING. + METHODS format_before_quality FOR TESTING. + METHODS date_before_quality FOR TESTING. + METHODS germination_before_moisture FOR TESTING. +ENDCLASS. + + +CLASS {{p}}seed_lot_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}seed_lot_checker( ). + ENDMETHOD. + + METHOD valid_lot. + rs_lot = VALUE #( lot_no = 'WHEA123456' + germination = 90 + moisture = '11.0' + harvest_date = '20250501' ). + ENDMETHOD. + + METHOD releases_valid_lot. + TRY. + mo_cut->check_lot( is_lot = valid_lot( ) iv_check_date = c_check_date ). + CATCH {{p}}cx_seed_lot INTO DATA(lo_ex). + cl_abap_unit_assert=>fail( msg = lo_ex->get_message( ) ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_bad_lot_no. + DATA(ls_lot) = valid_lot( ). + ls_lot-lot_no = 'AB12345678'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + CATCH {{p}}cx_lot_format INTO DATA(lo_format). + cl_abap_unit_assert=>assert_equals( + exp = 'The lot number has an invalid format.' + act = lo_format->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_lower_case_lot_no. + DATA(ls_lot) = valid_lot( ). + ls_lot-lot_no = 'whea123456'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + CATCH {{p}}cx_lot_format INTO DATA(lo_format). + cl_abap_unit_assert=>assert_equals( + exp = 'The lot number has an invalid format.' + act = lo_format->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_initial_harvest. + DATA(ls_lot) = valid_lot( ). + CLEAR ls_lot-harvest_date. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + CATCH {{p}}cx_lot_date INTO DATA(lo_date). + cl_abap_unit_assert=>assert_equals( + exp = 'The harvest date is not valid for the release check.' + act = lo_date->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_future_harvest. + DATA(ls_lot) = valid_lot( ). + ls_lot-harvest_date = '20250602'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + CATCH {{p}}cx_lot_date INTO DATA(lo_date). + cl_abap_unit_assert=>assert_equals( + exp = 'The harvest date is not valid for the release check.' + act = lo_date->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_old_harvest. + DATA(ls_lot) = valid_lot( ). + ls_lot-harvest_date = '20240531'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + CATCH {{p}}cx_lot_date INTO DATA(lo_date). + cl_abap_unit_assert=>assert_equals( + exp = 'The harvest date is not valid for the release check.' + act = lo_date->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_germination. + DATA(ls_lot) = valid_lot( ). + ls_lot-germination = 84. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + CATCH {{p}}cx_lot_germination INTO DATA(lo_germ). + cl_abap_unit_assert=>assert_equals( + exp = 'The germination rate is below the minimum of 85 percent.' + act = lo_germ->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_high_moisture. + DATA(ls_lot) = valid_lot( ). + ls_lot-moisture = '12.1'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_MOISTURE expected` ). + CATCH {{p}}cx_lot_moisture INTO DATA(lo_moist). + cl_abap_unit_assert=>assert_equals( + exp = 'The moisture is above the maximum of 12.0 percent.' + act = lo_moist->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_MOISTURE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_limit_values. + DATA(ls_lot) = valid_lot( ). + ls_lot-germination = 85. + ls_lot-moisture = '12.0'. + ls_lot-harvest_date = '20240601'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + CATCH {{p}}cx_seed_lot INTO DATA(lo_ex). + cl_abap_unit_assert=>fail( msg = lo_ex->get_message( ) ). + ENDTRY. + ENDMETHOD. + + METHOD format_before_quality. + DATA(ls_lot) = valid_lot( ). + ls_lot-lot_no = '1234567890'. + ls_lot-germination = 10. + ls_lot-moisture = '20.0'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + CATCH {{p}}cx_lot_format INTO DATA(lo_format). + cl_abap_unit_assert=>assert_equals( + exp = 'The lot number has an invalid format.' + act = lo_format->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + ENDTRY. + ENDMETHOD. + + METHOD date_before_quality. + DATA(ls_lot) = valid_lot( ). + ls_lot-harvest_date = '20240531'. + ls_lot-germination = 10. + ls_lot-moisture = '20.0'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + CATCH {{p}}cx_lot_date INTO DATA(lo_date). + cl_abap_unit_assert=>assert_equals( + exp = 'The harvest date is not valid for the release check.' + act = lo_date->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD germination_before_moisture. + DATA(ls_lot) = valid_lot( ). + ls_lot-germination = 10. + ls_lot-moisture = '20.0'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + CATCH {{p}}cx_lot_germination INTO DATA(lo_germ). + cl_abap_unit_assert=>assert_equals( + exp = 'The germination rate is below the minimum of 85 percent.' + act = lo_germ->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/mutation.json b/tasks_gen/train/G1084/mutation.json new file mode 100644 index 0000000..ab23180 --- /dev/null +++ b/tasks_gen/train/G1084/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1084", + "mutants": [ + { + "object": "{{P}}SEED_LOT_CHECKER", + "mutant": "line 49: OR -> AND (logic)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "FORMAT_BEFORE_QUALITY", + "REJECTS_BAD_LOT_NO", + "REJECTS_LOWER_CASE_LOT_NO" + ] + }, + { + "object": "{{P}}SEED_LOT_CHECKER", + "mutant": "line 59: < -> >= (rel)", + "status": "killed", + "hidden": "6/12", + "failed_tests": [ + "ACCEPTS_LIMIT_VALUES", + "GERMINATION_BEFORE_MOISTURE", + "REJECTS_FUTURE_HARVEST", + "REJECTS_HIGH_MOISTURE", + "REJECTS_LOW_GERMINATION", + "RELEASES_VALID_LOT" + ] + }, + { + "object": "{{P}}SEED_LOT_CHECKER", + "mutant": "line 59: OR -> AND (logic)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "DATE_BEFORE_QUALITY", + "REJECTS_FUTURE_HARVEST", + "REJECTS_OLD_HARVEST" + ] + }, + { + "object": "{{P}}SEED_LOT_CHECKER", + "mutant": "line 59: > -> <= (rel)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "ACCEPTS_LIMIT_VALUES", + "DATE_BEFORE_QUALITY", + "GERMINATION_BEFORE_MOISTURE", + "REJECTS_HIGH_MOISTURE", + "REJECTS_LOW_GERMINATION", + "REJECTS_OLD_HARVEST", + "RELEASES_VALID_LOT" + ] + }, + { + "object": "{{P}}SEED_LOT_CHECKER", + "mutant": "line 71: > -> <= (rel)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "ACCEPTS_LIMIT_VALUES", + "REJECTS_HIGH_MOISTURE", + "RELEASES_VALID_LOT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1084/reference/seed_lot_checker.clas.abap b/tasks_gen/train/G1084/reference/seed_lot_checker.clas.abap new file mode 100644 index 0000000..0df7ac2 --- /dev/null +++ b/tasks_gen/train/G1084/reference/seed_lot_checker.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}seed_lot_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. + + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + + PRIVATE SECTION. + CONSTANTS: + c_min_germination TYPE i VALUE 85, + c_max_moisture TYPE p LENGTH 4 DECIMALS 1 VALUE '12.0', + c_max_age_days TYPE i VALUE 365. + + METHODS check_lot_no + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_harvest_date + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{p}}cx_seed_lot. + METHODS check_germination + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. + METHODS check_moisture + IMPORTING is_lot TYPE ty_lot + RAISING {{p}}cx_seed_lot. +ENDCLASS. + + +CLASS {{p}}seed_lot_checker IMPLEMENTATION. + METHOD check_lot. + check_lot_no( is_lot ). + check_harvest_date( is_lot = is_lot iv_check_date = iv_check_date ). + check_germination( is_lot ). + check_moisture( is_lot ). + ENDMETHOD. + + METHOD check_lot_no. + DATA(lv_letters) = substring( val = is_lot-lot_no len = 4 ). + DATA(lv_digits) = substring( val = is_lot-lot_no off = 4 len = 6 ). + IF lv_letters CN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' OR lv_digits CN '0123456789'. + RAISE EXCEPTION TYPE {{p}}cx_lot_format. + ENDIF. + ENDMETHOD. + + METHOD check_harvest_date. + IF is_lot-harvest_date IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + DATA(lv_age_in_days) = iv_check_date - is_lot-harvest_date. + IF lv_age_in_days < 0 OR lv_age_in_days > c_max_age_days. + RAISE EXCEPTION TYPE {{p}}cx_lot_date. + ENDIF. + ENDMETHOD. + + METHOD check_germination. + IF is_lot-germination < c_min_germination. + RAISE EXCEPTION TYPE {{p}}cx_lot_germination. + ENDIF. + ENDMETHOD. + + METHOD check_moisture. + IF is_lot-moisture > c_max_moisture. + RAISE EXCEPTION TYPE {{p}}cx_lot_moisture. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/reference/seed_lot_checker.testclasses.abap b/tasks_gen/train/G1084/reference/seed_lot_checker.testclasses.abap new file mode 100644 index 0000000..c6c7d2b --- /dev/null +++ b/tasks_gen/train/G1084/reference/seed_lot_checker.testclasses.abap @@ -0,0 +1,109 @@ +CLASS ltc_seed_lot_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS c_check_date TYPE d VALUE '20250601'. + DATA mo_cut TYPE REF TO {{p}}seed_lot_checker. + METHODS setup. + METHODS valid_lot + RETURNING VALUE(rs_lot) TYPE {{p}}seed_lot_checker=>ty_lot. + METHODS releases_valid_lot FOR TESTING. + METHODS rejects_bad_lot_no FOR TESTING. + METHODS rejects_old_harvest FOR TESTING. + METHODS rejects_low_germination FOR TESTING. + METHODS rejects_high_moisture FOR TESTING. + METHODS accepts_limit_values FOR TESTING. +ENDCLASS. + + +CLASS ltc_seed_lot_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}seed_lot_checker( ). + ENDMETHOD. + + METHOD valid_lot. + rs_lot = VALUE #( lot_no = 'WHEA123456' + germination = 90 + moisture = '11.0' + harvest_date = '20250501' ). + ENDMETHOD. + + METHOD releases_valid_lot. + TRY. + mo_cut->check_lot( is_lot = valid_lot( ) iv_check_date = c_check_date ). + CATCH {{p}}cx_seed_lot INTO DATA(lo_ex). + cl_abap_unit_assert=>fail( msg = lo_ex->get_message( ) ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_bad_lot_no. + DATA(ls_lot) = valid_lot( ). + ls_lot-lot_no = 'AB12345678'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + CATCH {{p}}cx_lot_format INTO DATA(lo_format). + cl_abap_unit_assert=>assert_equals( + exp = 'The lot number has an invalid format.' + act = lo_format->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_FORMAT expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_old_harvest. + DATA(ls_lot) = valid_lot( ). + ls_lot-harvest_date = '20240531'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + CATCH {{p}}cx_lot_date INTO DATA(lo_date). + cl_abap_unit_assert=>assert_equals( + exp = 'The harvest date is not valid for the release check.' + act = lo_date->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_DATE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_low_germination. + DATA(ls_lot) = valid_lot( ). + ls_lot-germination = 84. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + CATCH {{p}}cx_lot_germination INTO DATA(lo_germ). + cl_abap_unit_assert=>assert_equals( + exp = 'The germination rate is below the minimum of 85 percent.' + act = lo_germ->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_GERMINATION expected` ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_high_moisture. + DATA(ls_lot) = valid_lot( ). + ls_lot-moisture = '12.1'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + cl_abap_unit_assert=>fail( msg = `CX_LOT_MOISTURE expected` ). + CATCH {{p}}cx_lot_moisture INTO DATA(lo_moist). + cl_abap_unit_assert=>assert_equals( + exp = 'The moisture is above the maximum of 12.0 percent.' + act = lo_moist->get_message( ) ). + CATCH {{p}}cx_seed_lot. + cl_abap_unit_assert=>fail( msg = `CX_LOT_MOISTURE expected` ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_limit_values. + DATA(ls_lot) = valid_lot( ). + ls_lot-germination = 85. + ls_lot-moisture = '12.0'. + ls_lot-harvest_date = '20240601'. + TRY. + mo_cut->check_lot( is_lot = ls_lot iv_check_date = c_check_date ). + CATCH {{p}}cx_seed_lot INTO DATA(lo_ex). + cl_abap_unit_assert=>fail( msg = lo_ex->get_message( ) ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/seed/cx_lot_date.clas.abap b/tasks_gen/train/G1084/seed/cx_lot_date.clas.abap new file mode 100644 index 0000000..5deb0b6 --- /dev/null +++ b/tasks_gen/train/G1084/seed/cx_lot_date.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_lot_date DEFINITION PUBLIC INHERITING FROM {{p}}cx_seed_lot CREATE PUBLIC. + PUBLIC SECTION. + CONSTANTS c_text TYPE string VALUE 'The harvest date is not valid for the release check.'. + METHODS constructor. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_lot_date IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = c_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/seed/cx_lot_format.clas.abap b/tasks_gen/train/G1084/seed/cx_lot_format.clas.abap new file mode 100644 index 0000000..d9abe3b --- /dev/null +++ b/tasks_gen/train/G1084/seed/cx_lot_format.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_lot_format DEFINITION PUBLIC INHERITING FROM {{p}}cx_seed_lot CREATE PUBLIC. + PUBLIC SECTION. + CONSTANTS c_text TYPE string VALUE 'The lot number has an invalid format.'. + METHODS constructor. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_lot_format IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = c_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/seed/cx_lot_germination.clas.abap b/tasks_gen/train/G1084/seed/cx_lot_germination.clas.abap new file mode 100644 index 0000000..e3bce45 --- /dev/null +++ b/tasks_gen/train/G1084/seed/cx_lot_germination.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_lot_germination DEFINITION PUBLIC INHERITING FROM {{p}}cx_seed_lot CREATE PUBLIC. + PUBLIC SECTION. + CONSTANTS c_text TYPE string VALUE 'The germination rate is below the minimum of 85 percent.'. + METHODS constructor. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_lot_germination IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = c_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/seed/cx_lot_moisture.clas.abap b/tasks_gen/train/G1084/seed/cx_lot_moisture.clas.abap new file mode 100644 index 0000000..b4e9a8f --- /dev/null +++ b/tasks_gen/train/G1084/seed/cx_lot_moisture.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_lot_moisture DEFINITION PUBLIC INHERITING FROM {{p}}cx_seed_lot CREATE PUBLIC. + PUBLIC SECTION. + CONSTANTS c_text TYPE string VALUE 'The moisture is above the maximum of 12.0 percent.'. + METHODS constructor. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_lot_moisture IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = c_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/seed/cx_seed_lot.clas.abap b/tasks_gen/train/G1084/seed/cx_seed_lot.clas.abap new file mode 100644 index 0000000..0ac68d3 --- /dev/null +++ b/tasks_gen/train/G1084/seed/cx_seed_lot.clas.abap @@ -0,0 +1,21 @@ +CLASS {{p}}cx_seed_lot DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_text TYPE string. + METHODS get_message + RETURNING VALUE(rv_text) TYPE string. + PRIVATE SECTION. + DATA mv_text TYPE string. +ENDCLASS. + + +CLASS {{p}}cx_seed_lot IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = iv_text. + ENDMETHOD. + + METHOD get_message. + rv_text = mv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1084/spec.md b/tasks_gen/train/G1084/spec.md new file mode 100644 index 0000000..60946ff --- /dev/null +++ b/tasks_gen/train/G1084/spec.md @@ -0,0 +1,60 @@ +# 1. Goal +A seed company releases seed lots from its warehouse. Before the warehouse +releases a lot, it calls a check for that lot and for one check date. The check +raises a class-based exception with a message when the lot must not be released. + +# 2. Open questions +None. + +# 3. Context +- The exception classes exist in package $TMP and are active: + - {{P}}CX_SEED_LOT is the superclass of the other four classes. It inherits + from CX_STATIC_CHECK. It has the public method GET_MESSAGE that returns the + message text of the exception. + - {{P}}CX_LOT_FORMAT, {{P}}CX_LOT_DATE, {{P}}CX_LOT_GERMINATION and + {{P}}CX_LOT_MOISTURE inherit from {{P}}CX_SEED_LOT. Each of them has a + constant C_TEXT with its message text and a constructor without parameters. +- The warehouse terminal calls the check for one lot and for one check date. + +# 4. Contract +- Create the class {{P}}SEED_LOT_CHECKER in package $TMP. +- The class is public, final and has a public constructor without parameters. +- The class has this public type: + TYPES: BEGIN OF ty_lot, + lot_no TYPE c LENGTH 10, + germination TYPE i, + moisture TYPE p LENGTH 4 DECIMALS 1, + harvest_date TYPE d, + END OF ty_lot. +- The class has this public method: + METHODS check_lot + IMPORTING is_lot TYPE ty_lot + iv_check_date TYPE d + RAISING {{P}}CX_SEED_LOT. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Raise the exception of the first rule that +fails. If no rule fails, the method ends normally and the lot is released. +1. Lot number: positions 1 to 4 must be the letters A to Z. Positions 5 to 10 + must be the digits 0 to 9. If not, raise {{P}}CX_LOT_FORMAT. +2. Harvest date: the harvest date must not be initial. The harvest date must + not be later than the check date. The harvest date must not be earlier than + the check date minus 365 days. If one of these conditions is not true, raise + {{P}}CX_LOT_DATE. +3. Germination: the germination rate must be at least 85. If not, raise + {{P}}CX_LOT_GERMINATION. +4. Moisture: the moisture must be at most 12.0. If not, raise + {{P}}CX_LOT_MOISTURE. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- 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}}CX_SEED_LOT, {{P}}CX_LOT_FORMAT, + {{P}}CX_LOT_DATE, {{P}}CX_LOT_GERMINATION or {{P}}CX_LOT_MOISTURE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1084/task.json b/tasks_gen/train/G1084/task.json new file mode 100644 index 0000000..5204ed1 --- /dev/null +++ b/tasks_gen/train/G1084/task.json @@ -0,0 +1,78 @@ +{ + "id": "G1084", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}CX_SEED_LOT", + "file": "seed/cx_seed_lot.clas.abap", + "description": "Exception superclass with the message text" + }, + { + "type": "CLAS", + "name": "{{P}}CX_LOT_FORMAT", + "file": "seed/cx_lot_format.clas.abap", + "description": "Exception: the lot number has an invalid format" + }, + { + "type": "CLAS", + "name": "{{P}}CX_LOT_DATE", + "file": "seed/cx_lot_date.clas.abap", + "description": "Exception: the harvest date is not valid" + }, + { + "type": "CLAS", + "name": "{{P}}CX_LOT_GERMINATION", + "file": "seed/cx_lot_germination.clas.abap", + "description": "Exception: the germination rate is too low" + }, + { + "type": "CLAS", + "name": "{{P}}CX_LOT_MOISTURE", + "file": "seed/cx_lot_moisture.clas.abap", + "description": "Exception: the moisture is too high" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SEED_LOT_CHECKER" + } + ], + "out_of_scope": [ + "{{P}}CX_SEED_LOT", + "{{P}}CX_LOT_FORMAT", + "{{P}}CX_LOT_DATE", + "{{P}}CX_LOT_GERMINATION", + "{{P}}CX_LOT_MOISTURE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SEED_LOT_HIDDEN", + "file": "hidden/seed_lot_hidden.clas.abap", + "description": "Hidden tests for the seed lot release check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SEED_LOT_CHECKER", + "file": "reference/seed_lot_checker.clas.abap", + "description": "Seed lot release check", + "testclasses_file": "reference/seed_lot_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1085/faulty/m0_dlv_price.func.abap b/tasks_gen/train/G1085/faulty/m0_dlv_price.func.abap new file mode 100644 index 0000000..3357996 --- /dev/null +++ b/tasks_gen/train/G1085/faulty/m0_dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_true. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 12 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 5 + iv_distance * '0.30' + iv_weight * '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/faulty/m1_dlv_price.func.abap b/tasks_gen/train/G1085/faulty/m1_dlv_price.func.abap new file mode 100644 index 0000000..f7585b5 --- /dev/null +++ b/tasks_gen/train/G1085/faulty/m1_dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 13 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 5 + iv_distance * '0.30' + iv_weight * '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/faulty/m2_dlv_price.func.abap b/tasks_gen/train/G1085/faulty/m2_dlv_price.func.abap new file mode 100644 index 0000000..0aad179 --- /dev/null +++ b/tasks_gen/train/G1085/faulty/m2_dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 12 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price >= 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 5 + iv_distance * '0.30' + iv_weight * '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/faulty/m3_dlv_price.func.abap b/tasks_gen/train/G1085/faulty/m3_dlv_price.func.abap new file mode 100644 index 0000000..fe9caa4 --- /dev/null +++ b/tasks_gen/train/G1085/faulty/m3_dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 12 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 6 + iv_distance * '0.30' + iv_weight * '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/faulty/m4_dlv_price.func.abap b/tasks_gen/train/G1085/faulty/m4_dlv_price.func.abap new file mode 100644 index 0000000..6a9e1df --- /dev/null +++ b/tasks_gen/train/G1085/faulty/m4_dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 12 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 5 + iv_distance * '0.30' + iv_weight / '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/generation.json b/tasks_gen/train/G1085/generation.json new file mode 100644 index 0000000..421cd26 --- /dev/null +++ b/tasks_gen/train/G1085/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1085", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1085/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1085/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..fcac682 --- /dev/null +++ b/tasks_gen/train/G1085/hidden/t14_hidden.clas.abap @@ -0,0 +1,83 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + iv_price TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS express_price FOR TESTING. + METHODS standard_price FOR TESTING. + METHODS price_rounding FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS unknown_service FOR TESTING. + METHODS out_of_area FOR TESTING. + METHODS invalid_weight FOR TESTING. + METHODS skip_false_normal FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD call. + ev_price = iv_price. + CALL FUNCTION '{{P}}DLV_PRICE' + EXPORTING iv_service = iv_service iv_weight = iv_weight iv_distance = iv_distance + IMPORTING ev_skip = ev_skip + CHANGING cv_price = ev_price + EXCEPTIONS invalid_weight = 1 OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD express_price. + call( EXPORTING iv_service = `EX` iv_weight = 2 iv_distance = 10 + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 21 ) act = lv_price ). + ENDMETHOD. + + METHOD standard_price. + call( EXPORTING iv_service = `ST` iv_weight = 5 iv_distance = 20 + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = lv_price ). + ENDMETHOD. + + METHOD price_rounding. + call( EXPORTING iv_service = `EX` iv_weight = '1.25' iv_distance = '7.7' + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.50' ) act = lv_price ). + ENDMETHOD. + + METHOD minimum_charge. + call( EXPORTING iv_service = `ST` iv_weight = '0.5' iv_distance = 1 + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 6 ) act = lv_price ). + ENDMETHOD. + + METHOD unknown_service. + call( EXPORTING iv_service = `XX` iv_weight = 2 iv_distance = 10 + IMPORTING ev_skip = DATA(lv_skip) ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_price ). + ENDMETHOD. + + METHOD out_of_area. + call( EXPORTING iv_service = `EX` iv_weight = 2 iv_distance = 400 iv_price = 7 + IMPORTING ev_skip = DATA(lv_skip) ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_price ). + ENDMETHOD. + + METHOD invalid_weight. + call( EXPORTING iv_service = `EX` iv_weight = 0 iv_distance = 10 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD skip_false_normal. + call( EXPORTING iv_service = `EX` iv_weight = 2 iv_distance = 10 + IMPORTING ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1085/mutation.json b/tasks_gen/train/G1085/mutation.json new file mode 100644 index 0000000..ea9d0fe --- /dev/null +++ b/tasks_gen/train/G1085/mutation.json @@ -0,0 +1,57 @@ +{ + "task": "G1085", + "mutants": [ + { + "object": "{{P}}DLV_PRICE", + "mutant": "line 13: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "7/8", + "failed_tests": [ + "SKIP_FALSE_NORMAL" + ] + }, + { + "object": "{{P}}DLV_PRICE", + "mutant": "line 23: 12 -> 13 (const)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "EXPRESS_PRICE", + "PRICE_ROUNDING" + ] + }, + { + "object": "{{P}}DLV_PRICE", + "mutant": "line 25: < -> >= (rel)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "EXPRESS_PRICE", + "PRICE_ROUNDING" + ] + }, + { + "object": "{{P}}DLV_PRICE", + "mutant": "line 29: 5 -> 6 (const)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "MINIMUM_CHARGE", + "STANDARD_PRICE" + ] + }, + { + "object": "{{P}}DLV_PRICE", + "mutant": "line 29: * -> / (arith)", + "status": "killed", + "hidden": "7/8", + "failed_tests": [ + "STANDARD_PRICE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1085/reference/dlv_price.func.abap b/tasks_gen/train/G1085/reference/dlv_price.func.abap new file mode 100644 index 0000000..93092db --- /dev/null +++ b/tasks_gen/train/G1085/reference/dlv_price.func.abap @@ -0,0 +1,39 @@ +FUNCTION {{p}}dlv_price + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + IF iv_distance > 300. + ev_skip = abap_true. + RETURN. + ENDIF. + CASE iv_service. + WHEN 'EX'. + cv_price = round( val = 12 + iv_distance * '0.60' + iv_weight * '1.50' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN 'ST'. + cv_price = round( val = 5 + iv_distance * '0.30' + iv_weight * '0.80' + dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < 6. + cv_price = 6. + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + cv_price = 0. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/reference/t14_test.clas.abap b/tasks_gen/train/G1085/reference/t14_test.clas.abap new file mode 100644 index 0000000..fb295d6 --- /dev/null +++ b/tasks_gen/train/G1085/reference/t14_test.clas.abap @@ -0,0 +1,55 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_for + IMPORTING iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + RETURNING VALUE(rv_price) TYPE decfloat34. + METHODS express_price FOR TESTING. + METHODS standard_price FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS out_of_area_skips FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD price_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}DLV_PRICE' + EXPORTING iv_service = iv_service iv_weight = iv_weight iv_distance = iv_distance + IMPORTING ev_skip = lv_skip + CHANGING cv_price = rv_price + EXCEPTIONS invalid_weight = 1 OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD express_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 21 ) + act = price_for( iv_service = `EX` iv_weight = 2 iv_distance = 10 ) ). + ENDMETHOD. + + METHOD standard_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 15 ) + act = price_for( iv_service = `ST` iv_weight = 5 iv_distance = 20 ) ). + ENDMETHOD. + + METHOD minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 6 ) + act = price_for( iv_service = `ST` iv_weight = '0.5' iv_distance = 1 ) ). + ENDMETHOD. + + METHOD out_of_area_skips. + DATA lv_skip TYPE abap_bool. + DATA lv_price TYPE decfloat34 VALUE 7. + CALL FUNCTION '{{P}}DLV_PRICE' + EXPORTING iv_service = `EX` iv_weight = 2 iv_distance = 400 + IMPORTING ev_skip = lv_skip + CHANGING cv_price = lv_price. + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1085/seed/dlv_sample.func.abap b/tasks_gen/train/G1085/seed/dlv_sample.func.abap new file mode 100644 index 0000000..1746679 --- /dev/null +++ b/tasks_gen/train/G1085/seed/dlv_sample.func.abap @@ -0,0 +1,18 @@ +FUNCTION {{p}}dlv_sample + IMPORTING + iv_service TYPE string + iv_weight TYPE decfloat34 + iv_distance TYPE decfloat34 + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_weight. + + ev_skip = abap_false. + IF iv_service IS INITIAL OR iv_weight < 0 OR iv_distance < 0. + RAISE invalid_weight. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1085/spec.md b/tasks_gen/train/G1085/spec.md new file mode 100644 index 0000000..83c7204 --- /dev/null +++ b/tasks_gen/train/G1085/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +Calculate the price of a same-day delivery for a courier company. The dispatch +program calls one event module for each delivery order. The event module must +have the same interface as the sample event module, because the dispatch program +calls all event modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_DLV exists in package $TMP. +- Function module {{P}}DLV_SAMPLE exists in this group. It is the sample event + module. Its interface is the interface that all event modules must use. Read + the interface of {{P}}DLV_SAMPLE before you start. + +# 4. Contract +- Create the function module {{P}}DLV_PRICE in function group {{P}}FG_DLV. +- Use exactly the interface of {{P}}DLV_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the weight is less than or equal to 0, raise the exception INVALID_WEIGHT. +2. If the distance is more than 300 km, the delivery is out of the service area. + Set the skip flag to true. Do not change the price. Stop here. Do not apply + rules 3 to 8. +3. Set the skip flag to false. +4. Service EX (express): the price is 12.00 plus 0.60 for each km plus 1.50 for + each kg. +5. Service ST (standard): the price is 5.00 plus 0.30 for each km plus 0.80 for + each kg. +6. Any other service: set the skip flag to true and set the price to 0. Do not + apply rules 7 and 8. +7. Round the price half up to 2 decimals. +8. The minimum price is 6.00. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Methods below 40 statements. No ATC priority 1 + or 2 findings. +- Out of scope: do not change {{P}}DLV_SAMPLE. + +# 7. Acceptance +- The function module {{P}}DLV_PRICE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1085/task.json b/tasks_gen/train/G1085/task.json new file mode 100644 index 0000000..664f3a2 --- /dev/null +++ b/tasks_gen/train/G1085/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1085", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_DLV", + "description": "Delivery fee events" + }, + { + "type": "FUNC", + "name": "{{P}}DLV_SAMPLE", + "functionGroup": "{{P}}FG_DLV", + "file": "seed/dlv_sample.func.abap", + "description": "Sample delivery event module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}DLV_PRICE", + "functionGroup": "{{P}}FG_DLV", + "params": [ + { + "name": "iv_service", + "type": "string" + }, + { + "name": "iv_weight", + "type": "decfloat34" + }, + { + "name": "iv_distance", + "type": "decfloat34" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}DLV_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}DLV_PRICE", + "functionGroup": "{{P}}FG_DLV", + "file": "reference/dlv_price.func.abap", + "description": "Delivery price event module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1086/faulty/m0_wood_fee.func.abap b/tasks_gen/train/G1086/faulty/m0_wood_fee.func.abap new file mode 100644 index 0000000..817f87b --- /dev/null +++ b/tasks_gen/train/G1086/faulty/m0_wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance >= 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type = 'MX'. + ev_skip = abap_true. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km * lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/faulty/m1_wood_fee.func.abap b/tasks_gen/train/G1086/faulty/m1_wood_fee.func.abap new file mode 100644 index 0000000..4996ad2 --- /dev/null +++ b/tasks_gen/train/G1086/faulty/m1_wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance < 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type <> 'MX'. + ev_skip = abap_true. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km * lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/faulty/m2_wood_fee.func.abap b/tasks_gen/train/G1086/faulty/m2_wood_fee.func.abap new file mode 100644 index 0000000..c69a847 --- /dev/null +++ b/tasks_gen/train/G1086/faulty/m2_wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance < 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type = 'MX'. + ev_skip = abap_false. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km * lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/faulty/m3_wood_fee.func.abap b/tasks_gen/train/G1086/faulty/m3_wood_fee.func.abap new file mode 100644 index 0000000..ff15a15 --- /dev/null +++ b/tasks_gen/train/G1086/faulty/m3_wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance < 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type = 'MX'. + ev_skip = abap_true. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km / lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/faulty/m4_wood_fee.func.abap b/tasks_gen/train/G1086/faulty/m4_wood_fee.func.abap new file mode 100644 index 0000000..797e701 --- /dev/null +++ b/tasks_gen/train/G1086/faulty/m4_wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance < 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type = 'MX'. + ev_skip = abap_true. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km * lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 3 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/generation.json b/tasks_gen/train/G1086/generation.json new file mode 100644 index 0000000..c89ad65 --- /dev/null +++ b/tasks_gen/train/G1086/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1086", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1086/hidden/wood_hidden.clas.abap b/tasks_gen/train/G1086/hidden/wood_hidden.clas.abap new file mode 100644 index 0000000..d882e48 --- /dev/null +++ b/tasks_gen/train/G1086/hidden/wood_hidden.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}wood_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_of + IMPORTING iv_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i DEFAULT 0 + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS beech_price FOR TESTING. + METHODS type_prices FOR TESTING. + METHODS delivery_charge FOR TESTING. + METHODS no_charge_at_20 FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS unknown_type_minimum FOR TESTING. + METHODS mixed_skips FOR TESTING. + METHODS skip_false_for_beech FOR TESTING. + METHODS invalid_volume FOR TESTING. + METHODS invalid_distance FOR TESTING. +ENDCLASS. + + +CLASS {{p}}wood_hidden IMPLEMENTATION. + METHOD fee_of. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}WOOD_FEE' + EXPORTING + iv_wood_type = iv_type + iv_volume = iv_volume + iv_distance = iv_distance + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_volume = 1 + invalid_distance = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD beech_price. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '240'. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '2' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD type_prices. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '150'. + fee_of( EXPORTING iv_type = 'OK' iv_volume = '1' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + lv_exp = '180'. + fee_of( EXPORTING iv_type = 'SP' iv_volume = '2' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD delivery_charge. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '244.50'. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '2' iv_distance = 30 IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD no_charge_at_20. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '240'. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '2' iv_distance = 20 IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '80'. + fee_of( EXPORTING iv_type = 'SP' iv_volume = '0.5' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD rounding_half_up. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '100.13'. + fee_of( EXPORTING iv_type = 'SP' iv_volume = '1.1125' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD unknown_type_minimum. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '80'. + fee_of( EXPORTING iv_type = 'XX' iv_volume = '2' IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD mixed_skips. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '7'. + fee_of( EXPORTING iv_type = 'MX' iv_volume = '2' iv_distance = 10 iv_fee = '7' + IMPORTING ev_skip = lv_skip ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD skip_false_for_beech. + DATA lv_skip TYPE abap_bool. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '2' IMPORTING ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD invalid_volume. + DATA lv_rc TYPE sysubrc. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '0' IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD invalid_distance. + DATA lv_rc TYPE sysubrc. + fee_of( EXPORTING iv_type = 'BE' iv_volume = '2' iv_distance = -1 IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1086/mutation.json b/tasks_gen/train/G1086/mutation.json new file mode 100644 index 0000000..803f314 --- /dev/null +++ b/tasks_gen/train/G1086/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1086", + "mutants": [ + { + "object": "{{P}}WOOD_FEE", + "mutant": "line 23: < -> >= (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BEECH_PRICE", + "DELIVERY_CHARGE", + "INVALID_DISTANCE", + "MINIMUM_FEE", + "MIXED_SKIPS", + "NO_CHARGE_AT_20", + "ROUNDING_HALF_UP", + "TYPE_PRICES", + "UNKNOWN_TYPE_MINIMUM" + ] + }, + { + "object": "{{P}}WOOD_FEE", + "mutant": "line 28: = -> <> (eq)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BEECH_PRICE", + "DELIVERY_CHARGE", + "MINIMUM_FEE", + "MIXED_SKIPS", + "NO_CHARGE_AT_20", + "ROUNDING_HALF_UP", + "SKIP_FALSE_FOR_BEECH", + "TYPE_PRICES", + "UNKNOWN_TYPE_MINIMUM" + ] + }, + { + "object": "{{P}}WOOD_FEE", + "mutant": "line 29: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "MIXED_SKIPS" + ] + }, + { + "object": "{{P}}WOOD_FEE", + "mutant": "line 47: * -> / (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "DELIVERY_CHARGE" + ] + }, + { + "object": "{{P}}WOOD_FEE", + "mutant": "line 50: 2 -> 3 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "ROUNDING_HALF_UP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1086/reference/wood_fee.func.abap b/tasks_gen/train/G1086/reference/wood_fee.func.abap new file mode 100644 index 0000000..299f855 --- /dev/null +++ b/tasks_gen/train/G1086/reference/wood_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}wood_fee + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + CONSTANTS lc_charge TYPE decfloat34 VALUE '0.45'. + CONSTANTS lc_minimum TYPE decfloat34 VALUE '80'. + DATA lv_price TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_km TYPE decfloat34. + + IF iv_volume <= 0. + RAISE invalid_volume. + ENDIF. + IF iv_distance < 0. + RAISE invalid_distance. + ENDIF. + + ev_skip = abap_false. + IF iv_wood_type = 'MX'. + ev_skip = abap_true. + RETURN. + ENDIF. + + CASE iv_wood_type. + WHEN 'BE'. + lv_price = 120. + WHEN 'OK'. + lv_price = 150. + WHEN 'SP'. + lv_price = 90. + WHEN OTHERS. + lv_price = 0. + ENDCASE. + + lv_fee = iv_volume * lv_price. + IF iv_distance > 20. + lv_km = iv_distance - 20. + lv_fee = lv_fee + lv_km * lc_charge. + ENDIF. + + cv_fee = round( val = lv_fee dec = 2 ). + IF cv_fee < lc_minimum. + cv_fee = lc_minimum. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/reference/wood_test.clas.abap b/tasks_gen/train/G1086/reference/wood_test.clas.abap new file mode 100644 index 0000000..b6b431c --- /dev/null +++ b/tasks_gen/train/G1086/reference/wood_test.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}wood_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i DEFAULT 0 + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS beech_fee FOR TESTING. + METHODS delivery_charge FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS mixed_skips FOR TESTING. + METHODS invalid_volume FOR TESTING. +ENDCLASS. + + +CLASS {{p}}wood_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}WOOD_FEE' + EXPORTING + iv_wood_type = iv_type + iv_volume = iv_volume + iv_distance = iv_distance + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_volume = 1 + invalid_distance = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD beech_fee. + DATA lv_exp TYPE decfloat34. + lv_exp = '240'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'BE' iv_volume = '2' ) ). + ENDMETHOD. + + METHOD delivery_charge. + DATA lv_exp TYPE decfloat34. + lv_exp = '244.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'BE' iv_volume = '2' iv_distance = 30 ) ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_exp TYPE decfloat34. + lv_exp = '80'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'SP' iv_volume = '0.5' ) ). + ENDMETHOD. + + METHOD mixed_skips. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_exp = '7'. + lv_fee = '7'. + CALL FUNCTION '{{P}}WOOD_FEE' + EXPORTING + iv_wood_type = 'MX' + iv_volume = '2' + iv_distance = 10 + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = lv_fee. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD invalid_volume. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}WOOD_FEE' + EXPORTING + iv_wood_type = 'BE' + iv_volume = '0' + iv_distance = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_volume = 1 + invalid_distance = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1086/seed/wood_sample.func.abap b/tasks_gen/train/G1086/seed/wood_sample.func.abap new file mode 100644 index 0000000..310f7c2 --- /dev/null +++ b/tasks_gen/train/G1086/seed/wood_sample.func.abap @@ -0,0 +1,16 @@ +FUNCTION {{p}}wood_sample + IMPORTING + iv_wood_type TYPE string + iv_volume TYPE decfloat34 + iv_distance TYPE i + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_volume + invalid_distance. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1086/spec.md b/tasks_gen/train/G1086/spec.md new file mode 100644 index 0000000..d403eca --- /dev/null +++ b/tasks_gen/train/G1086/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +Calculate the price of a firewood delivery for a firewood merchant. The order +program calls one function module for each delivery order. The function module +must have the same interface as the sample function module, because the order +program calls all function modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_WOOD exists in package $TMP. +- Function module {{P}}WOOD_SAMPLE exists in this group. It is the sample + function module. Its interface is the interface that all function modules of + the order program must use. + +# 4. Contract +- Create the function module {{P}}WOOD_FEE in function group {{P}}FG_WOOD. +- Use exactly the interface of {{P}}WOOD_SAMPLE: the same parameter names, the + same parameter kinds, the same types, and the same exceptions. + +# 5. Business rules +Apply the rules in this order. +1. If iv_volume is less than or equal to 0, raise INVALID_VOLUME. +2. If iv_distance is less than 0, raise INVALID_DISTANCE. +3. Set ev_skip to false, except in rule 4. +4. If iv_wood_type is 'MX', set ev_skip to true and do not change cv_fee. +5. The price for one cubic metre is 120.00 for wood type 'BE', 150.00 for wood + type 'OK', and 90.00 for wood type 'SP'. For every other wood type the price + for one cubic metre is 0. +6. The wood price is iv_volume multiplied by the price for one cubic metre. +7. If iv_distance is greater than 20, the delivery charge is 0.45 for each + kilometre above 20. Otherwise the delivery charge is 0. +8. The fee is the wood price plus the delivery charge, rounded half up to two + decimals. +9. If the fee is less than 80.00, the fee is 80.00. + +# 6. Constraints +- Release target: 7.02. Use only syntax that release 7.02 supports. +- Coding standard: Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}WOOD_SAMPLE. + +# 7. Acceptance +- The function module {{P}}WOOD_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1086/task.json b/tasks_gen/train/G1086/task.json new file mode 100644 index 0000000..f86187a --- /dev/null +++ b/tasks_gen/train/G1086/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1086", + "category": "G", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v702", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_WOOD", + "description": "Function group for firewood delivery fees" + }, + { + "type": "FUNC", + "name": "{{P}}WOOD_SAMPLE", + "functionGroup": "{{P}}FG_WOOD", + "file": "seed/wood_sample.func.abap", + "description": "Sample function module. Its interface is the interface of all order modules." + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}WOOD_FEE", + "functionGroup": "{{P}}FG_WOOD", + "params": [ + { + "name": "iv_wood_type", + "type": "string" + }, + { + "name": "iv_volume", + "type": "decfloat34" + }, + { + "name": "iv_distance", + "type": "i" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}WOOD_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}WOOD_HIDDEN", + "file": "hidden/wood_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}WOOD_FEE", + "functionGroup": "{{P}}FG_WOOD", + "file": "reference/wood_fee.func.abap", + "description": "Firewood delivery fee" + }, + { + "type": "CLAS", + "name": "{{P}}WOOD_TEST", + "file": "reference/wood_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1087/generation.json b/tasks_gen/train/G1087/generation.json new file mode 100644 index 0000000..9dc434a --- /dev/null +++ b/tasks_gen/train/G1087/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1087", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1087/reference/tool_hire.clas.abap b/tasks_gen/train/G1087/reference/tool_hire.clas.abap new file mode 100644 index 0000000..64e5ee9 --- /dev/null +++ b/tasks_gen/train/G1087/reference/tool_hire.clas.abap @@ -0,0 +1,62 @@ +CLASS {{p}}tool_hire DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_tool_hire. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + tool TYPE {{p}}if_tool_hire=>ty_tool, + daily TYPE {{p}}if_tool_hire=>ty_amount, + weekly TYPE {{p}}if_tool_hire=>ty_amount, + monthly TYPE {{p}}if_tool_hire=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY tool. + + CONSTANTS: + c_week_days TYPE i VALUE 7, + c_month_days TYPE i VALUE 28, + c_max_days TYPE i VALUE 90. + + METHODS rates + RETURNING VALUE(rt_rate) TYPE tt_rate. + METHODS fee_for + IMPORTING is_rate TYPE ty_rate + iv_days TYPE {{p}}if_tool_hire=>ty_days + RETURNING VALUE(rv_fee) TYPE {{p}}if_tool_hire=>ty_amount. +ENDCLASS. + + +CLASS {{p}}tool_hire IMPLEMENTATION. + METHOD {{p}}if_tool_hire~calculate. + DATA(lt_rate) = rates( ). + ASSIGN lt_rate[ tool = iv_tool ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rs_result = VALUE #( fee = 0 + reason = {{p}}if_tool_hire=>c_reason-unknown_tool ). + RETURN. + ENDIF. + IF iv_days < 1 OR iv_days > c_max_days. + rs_result = VALUE #( fee = 0 + reason = {{p}}if_tool_hire=>c_reason-too_short ). + RETURN. + ENDIF. + rs_result = VALUE #( fee = fee_for( is_rate = iv_days = iv_days ) + reason = {{p}}if_tool_hire=>c_reason-ok ). + ENDMETHOD. + + METHOD rates. + rt_rate = VALUE #( ( tool = 'D' daily = 12 weekly = 60 monthly = 180 ) + ( tool = 'S' daily = 9 weekly = 45 monthly = 135 ) + ( tool = 'C' daily = 15 weekly = 75 monthly = 225 ) ). + ENDMETHOD. + + METHOD fee_for. + IF iv_days >= c_month_days. + rv_fee = is_rate-monthly. + ELSEIF iv_days >= c_week_days. + rv_fee = is_rate-weekly. + ELSE. + rv_fee = is_rate-daily * iv_days. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1087/reference/tool_hire.testclasses.abap b/tasks_gen/train/G1087/reference/tool_hire.testclasses.abap new file mode 100644 index 0000000..a1aad40 --- /dev/null +++ b/tasks_gen/train/G1087/reference/tool_hire.testclasses.abap @@ -0,0 +1,67 @@ +CLASS ltc_tool_hire DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE {{p}}if_tool_hire=>ty_amount. + DATA mo_cut TYPE REF TO {{p}}if_tool_hire. + METHODS setup. + METHODS daily_rate FOR TESTING. + METHODS weekly_rate FOR TESTING. + METHODS monthly_rate FOR TESTING. + METHODS unknown_tool FOR TESTING. + METHODS zero_days FOR TESTING. + METHODS over_maximum FOR TESTING. +ENDCLASS. + + +CLASS ltc_tool_hire IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tool_hire( ). + ENDMETHOD. + + METHOD daily_rate. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'D' iv_days = 3 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '36.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD weekly_rate. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'S' iv_days = 10 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '45.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD monthly_rate. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'C' iv_days = 30 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '225.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD unknown_tool. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'X' iv_days = 5 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-unknown_tool + act = ls_result-reason ). + ENDMETHOD. + + METHOD zero_days. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'D' iv_days = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-too_short + act = ls_result-reason ). + ENDMETHOD. + + METHOD over_maximum. + DATA(ls_result) = mo_cut->calculate( iv_tool = 'D' iv_days = 100 ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' ) + act = ls_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tool_hire=>c_reason-too_short + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1087/seed/if_tool_hire.intf.abap b/tasks_gen/train/G1087/seed/if_tool_hire.intf.abap new file mode 100644 index 0000000..3a9b086 --- /dev/null +++ b/tasks_gen/train/G1087/seed/if_tool_hire.intf.abap @@ -0,0 +1,21 @@ +INTERFACE {{p}}if_tool_hire PUBLIC. + TYPES ty_tool TYPE c LENGTH 1. + TYPES ty_days TYPE i. + TYPES ty_amount TYPE p LENGTH 13 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 12. + TYPES: + BEGIN OF ty_result, + fee TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown_tool TYPE ty_reason VALUE 'UNKNOWN_TOOL', + too_short TYPE ty_reason VALUE 'TOO_SHORT', + END OF c_reason. + METHODS calculate + IMPORTING iv_tool TYPE ty_tool + iv_days TYPE ty_days + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1087/spec.md b/tasks_gen/train/G1087/spec.md new file mode 100644 index 0000000..1870dfb --- /dev/null +++ b/tasks_gen/train/G1087/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A tool hire shop rents power tools to its customers. The checkout program calls +the class {{P}}TOOL_HIRE for each hire request before it prints the hire +contract. The program needs the fee of the hire and one reason code. + +# 2. Open questions +None. + +# 3. Context +- Interface {{P}}IF_TOOL_HIRE exists in package $TMP. It is active. +- The interface has these types: TY_TOOL (CHAR 1), TY_DAYS (integer), + TY_AMOUNT (packed, 13 digits, 2 decimals), TY_REASON (CHAR 12). +- The interface has the structure TY_RESULT with the components FEE and REASON. +- The interface has the constant structure C_REASON with the values OK, + UNKNOWN_TOOL and TOO_SHORT. +- The interface has one method: + CALCULATE IMPORTING iv_tool TYPE ty_tool + iv_days TYPE ty_days + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}TOOL_HIRE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_TOOL_HIRE. +- Do not add other public methods. + +# 5. Business rules +1. The tool category is one character. These categories exist: 'D' is a drill, + 'S' is a sander, 'C' is a tile cutter. Every other category is unknown. For + an unknown category return the reason UNKNOWN_TOOL and the fee 0.00. +2. A hire of less than 1 day is not possible. Return the reason TOO_SHORT and + the fee 0.00. +3. The daily rate is 12.00 for 'D', 9.00 for 'S' and 15.00 for 'C'. A hire of + 1 to 6 days costs the daily rate multiplied by the number of days. +4. A hire of 7 days or more costs the weekly rate instead of the daily rate. + The weekly rate is 60.00 for 'D', 45.00 for 'S' and 75.00 for 'C'. +5. A hire of 28 days or more costs the monthly rate instead of the weekly rate. + The monthly rate is 180.00 for 'D', 135.00 for 'S' and 225.00 for 'C'. +6. The shop accepts hires of at most 90 days. +7. For every accepted hire return the reason OK and the fee of the hire. +8. The checkout program calls the class for every hire request, also for + requests that the shop cannot accept. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- 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_TOOL_HIRE. + +# 7. Acceptance +- The class is active and has no syntax error. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1087/task.json b/tasks_gen/train/G1087/task.json new file mode 100644 index 0000000..ee7cb95 --- /dev/null +++ b/tasks_gen/train/G1087/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1087", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The spec states that the shop accepts hires of at most 90 days but does not say which fee and which reason code the class returns for a hire that is longer than 90 days.", + "keywords": [ + [ + "90", + "days" + ], + [ + "reason", + "fee" + ], + [ + "return", + "90" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_TOOL_HIRE", + "file": "seed/if_tool_hire.intf.abap", + "description": "Tool hire contract with types, reason codes and the calculate method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TOOL_HIRE", + "implements": "{{P}}IF_TOOL_HIRE" + } + ], + "out_of_scope": [ + "{{P}}IF_TOOL_HIRE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOOL_HIRE", + "file": "reference/tool_hire.clas.abap", + "description": "Tool hire fee calculator", + "testclasses_file": "reference/tool_hire.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1089/faulty/m1_keg_tour_merger.clas.abap b/tasks_gen/train/G1089/faulty/m1_keg_tour_merger.clas.abap new file mode 100644 index 0000000..2eaa87a --- /dev/null +++ b/tasks_gen/train/G1089/faulty/m1_keg_tour_merger.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}keg_tour_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_entry, + pub_id TYPE c LENGTH 8, + keg_type TYPE c LENGTH 4, + kegs TYPE i, + END OF ty_entry, + tt_entry TYPE STANDARD TABLE OF ty_entry WITH EMPTY KEY, + BEGIN OF ty_pub, + pub_id TYPE c LENGTH 8, + kegs TYPE i, + END OF ty_pub, + tt_pub TYPE STANDARD TABLE OF ty_pub WITH EMPTY KEY. + + METHODS merge_tours + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + + METHODS top_pubs + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. + + PRIVATE SECTION. + TYPES tt_agg TYPE HASHED TABLE OF ty_entry WITH UNIQUE KEY pub_id keg_type. + TYPES tt_pub_agg TYPE HASHED TABLE OF ty_pub WITH UNIQUE KEY pub_id. + + METHODS add_entries + IMPORTING it_tour TYPE tt_entry + CHANGING ct_agg TYPE tt_agg. + + METHODS add_pubs + IMPORTING it_tour TYPE tt_entry + CHANGING ct_pub TYPE tt_pub_agg. +ENDCLASS. + + +CLASS {{p}}keg_tour_merger IMPLEMENTATION. + METHOD merge_tours. + DATA lt_agg TYPE tt_agg. + add_entries( EXPORTING it_tour = it_tour_a CHANGING ct_agg = lt_agg ). + add_entries( EXPORTING it_tour = it_tour_b CHANGING ct_agg = lt_agg ). + rt_tour = lt_agg. + SORT rt_tour BY pub_id keg_type. + ENDMETHOD. + + METHOD top_pubs. + IF iv_count < 1. + RETURN. + ENDIF. + DATA lt_agg TYPE tt_pub_agg. + add_pubs( EXPORTING it_tour = it_tour CHANGING ct_pub = lt_agg ). + rt_pubs = lt_agg. + SORT rt_pubs BY kegs DESCENDING pub_id ASCENDING. + DATA(lv_from) = iv_count + 1. + IF lines( rt_pubs ) >= lv_from. + DELETE rt_pubs FROM lv_from. + ENDIF. + ENDMETHOD. + + METHOD add_entries. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs >= 1. + CONTINUE. + ENDIF. + DATA(ls_norm) = VALUE ty_entry( pub_id = to_upper( ls_entry-pub_id ) + keg_type = to_upper( ls_entry-keg_type ) + kegs = ls_entry-kegs ). + ASSIGN ct_agg[ pub_id = ls_norm-pub_id + keg_type = ls_norm-keg_type ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_norm-kegs. + ELSE. + INSERT ls_norm INTO TABLE ct_agg. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD add_pubs. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(lv_pub_id) = to_upper( ls_entry-pub_id ). + ASSIGN ct_pub[ pub_id = lv_pub_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_entry-kegs. + ELSE. + DATA(ls_new) = VALUE ty_pub( pub_id = lv_pub_id kegs = ls_entry-kegs ). + INSERT ls_new INTO TABLE ct_pub. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/faulty/m2_keg_tour_merger.clas.abap b/tasks_gen/train/G1089/faulty/m2_keg_tour_merger.clas.abap new file mode 100644 index 0000000..ade565c --- /dev/null +++ b/tasks_gen/train/G1089/faulty/m2_keg_tour_merger.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}keg_tour_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_entry, + pub_id TYPE c LENGTH 8, + keg_type TYPE c LENGTH 4, + kegs TYPE i, + END OF ty_entry, + tt_entry TYPE STANDARD TABLE OF ty_entry WITH EMPTY KEY, + BEGIN OF ty_pub, + pub_id TYPE c LENGTH 8, + kegs TYPE i, + END OF ty_pub, + tt_pub TYPE STANDARD TABLE OF ty_pub WITH EMPTY KEY. + + METHODS merge_tours + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + + METHODS top_pubs + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. + + PRIVATE SECTION. + TYPES tt_agg TYPE HASHED TABLE OF ty_entry WITH UNIQUE KEY pub_id keg_type. + TYPES tt_pub_agg TYPE HASHED TABLE OF ty_pub WITH UNIQUE KEY pub_id. + + METHODS add_entries + IMPORTING it_tour TYPE tt_entry + CHANGING ct_agg TYPE tt_agg. + + METHODS add_pubs + IMPORTING it_tour TYPE tt_entry + CHANGING ct_pub TYPE tt_pub_agg. +ENDCLASS. + + +CLASS {{p}}keg_tour_merger IMPLEMENTATION. + METHOD merge_tours. + DATA lt_agg TYPE tt_agg. + add_entries( EXPORTING it_tour = it_tour_a CHANGING ct_agg = lt_agg ). + add_entries( EXPORTING it_tour = it_tour_b CHANGING ct_agg = lt_agg ). + rt_tour = lt_agg. + SORT rt_tour BY pub_id keg_type. + ENDMETHOD. + + METHOD top_pubs. + IF iv_count < 1. + RETURN. + ENDIF. + DATA lt_agg TYPE tt_pub_agg. + add_pubs( EXPORTING it_tour = it_tour CHANGING ct_pub = lt_agg ). + rt_pubs = lt_agg. + SORT rt_pubs BY kegs DESCENDING pub_id ASCENDING. + DATA(lv_from) = iv_count + 1. + IF lines( rt_pubs ) >= lv_from. + DELETE rt_pubs FROM lv_from. + ENDIF. + ENDMETHOD. + + METHOD add_entries. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(ls_norm) = VALUE ty_entry( pub_id = to_upper( ls_entry-pub_id ) + keg_type = to_upper( ls_entry-keg_type ) + kegs = ls_entry-kegs ). + ASSIGN ct_agg[ pub_id = ls_norm-pub_id + keg_type = ls_norm-keg_type ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs - ls_norm-kegs. + ELSE. + INSERT ls_norm INTO TABLE ct_agg. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD add_pubs. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(lv_pub_id) = to_upper( ls_entry-pub_id ). + ASSIGN ct_pub[ pub_id = lv_pub_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_entry-kegs. + ELSE. + DATA(ls_new) = VALUE ty_pub( pub_id = lv_pub_id kegs = ls_entry-kegs ). + INSERT ls_new INTO TABLE ct_pub. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/faulty/m3_keg_tour_merger.clas.abap b/tasks_gen/train/G1089/faulty/m3_keg_tour_merger.clas.abap new file mode 100644 index 0000000..a61b821 --- /dev/null +++ b/tasks_gen/train/G1089/faulty/m3_keg_tour_merger.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}keg_tour_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_entry, + pub_id TYPE c LENGTH 8, + keg_type TYPE c LENGTH 4, + kegs TYPE i, + END OF ty_entry, + tt_entry TYPE STANDARD TABLE OF ty_entry WITH EMPTY KEY, + BEGIN OF ty_pub, + pub_id TYPE c LENGTH 8, + kegs TYPE i, + END OF ty_pub, + tt_pub TYPE STANDARD TABLE OF ty_pub WITH EMPTY KEY. + + METHODS merge_tours + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + + METHODS top_pubs + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. + + PRIVATE SECTION. + TYPES tt_agg TYPE HASHED TABLE OF ty_entry WITH UNIQUE KEY pub_id keg_type. + TYPES tt_pub_agg TYPE HASHED TABLE OF ty_pub WITH UNIQUE KEY pub_id. + + METHODS add_entries + IMPORTING it_tour TYPE tt_entry + CHANGING ct_agg TYPE tt_agg. + + METHODS add_pubs + IMPORTING it_tour TYPE tt_entry + CHANGING ct_pub TYPE tt_pub_agg. +ENDCLASS. + + +CLASS {{p}}keg_tour_merger IMPLEMENTATION. + METHOD merge_tours. + DATA lt_agg TYPE tt_agg. + add_entries( EXPORTING it_tour = it_tour_a CHANGING ct_agg = lt_agg ). + add_entries( EXPORTING it_tour = it_tour_b CHANGING ct_agg = lt_agg ). + rt_tour = lt_agg. + SORT rt_tour BY pub_id keg_type. + ENDMETHOD. + + METHOD top_pubs. + IF iv_count < 1. + RETURN. + ENDIF. + DATA lt_agg TYPE tt_pub_agg. + add_pubs( EXPORTING it_tour = it_tour CHANGING ct_pub = lt_agg ). + rt_pubs = lt_agg. + SORT rt_pubs BY kegs DESCENDING pub_id ASCENDING. + DATA(lv_from) = iv_count + 1. + IF lines( rt_pubs ) >= lv_from. + DELETE rt_pubs FROM lv_from. + ENDIF. + ENDMETHOD. + + METHOD add_entries. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(ls_norm) = VALUE ty_entry( pub_id = to_upper( ls_entry-pub_id ) + keg_type = to_upper( ls_entry-keg_type ) + kegs = ls_entry-kegs ). + ASSIGN ct_agg[ pub_id = ls_norm-pub_id + keg_type = ls_norm-keg_type ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_norm-kegs. + ELSE. + INSERT ls_norm INTO TABLE ct_agg. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD add_pubs. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs >= 1. + CONTINUE. + ENDIF. + DATA(lv_pub_id) = to_upper( ls_entry-pub_id ). + ASSIGN ct_pub[ pub_id = lv_pub_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_entry-kegs. + ELSE. + DATA(ls_new) = VALUE ty_pub( pub_id = lv_pub_id kegs = ls_entry-kegs ). + INSERT ls_new INTO TABLE ct_pub. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/faulty/m4_keg_tour_merger.clas.abap b/tasks_gen/train/G1089/faulty/m4_keg_tour_merger.clas.abap new file mode 100644 index 0000000..4146cca --- /dev/null +++ b/tasks_gen/train/G1089/faulty/m4_keg_tour_merger.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}keg_tour_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_entry, + pub_id TYPE c LENGTH 8, + keg_type TYPE c LENGTH 4, + kegs TYPE i, + END OF ty_entry, + tt_entry TYPE STANDARD TABLE OF ty_entry WITH EMPTY KEY, + BEGIN OF ty_pub, + pub_id TYPE c LENGTH 8, + kegs TYPE i, + END OF ty_pub, + tt_pub TYPE STANDARD TABLE OF ty_pub WITH EMPTY KEY. + + METHODS merge_tours + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + + METHODS top_pubs + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. + + PRIVATE SECTION. + TYPES tt_agg TYPE HASHED TABLE OF ty_entry WITH UNIQUE KEY pub_id keg_type. + TYPES tt_pub_agg TYPE HASHED TABLE OF ty_pub WITH UNIQUE KEY pub_id. + + METHODS add_entries + IMPORTING it_tour TYPE tt_entry + CHANGING ct_agg TYPE tt_agg. + + METHODS add_pubs + IMPORTING it_tour TYPE tt_entry + CHANGING ct_pub TYPE tt_pub_agg. +ENDCLASS. + + +CLASS {{p}}keg_tour_merger IMPLEMENTATION. + METHOD merge_tours. + DATA lt_agg TYPE tt_agg. + add_entries( EXPORTING it_tour = it_tour_a CHANGING ct_agg = lt_agg ). + add_entries( EXPORTING it_tour = it_tour_b CHANGING ct_agg = lt_agg ). + rt_tour = lt_agg. + SORT rt_tour BY pub_id keg_type. + ENDMETHOD. + + METHOD top_pubs. + IF iv_count < 1. + RETURN. + ENDIF. + DATA lt_agg TYPE tt_pub_agg. + add_pubs( EXPORTING it_tour = it_tour CHANGING ct_pub = lt_agg ). + rt_pubs = lt_agg. + SORT rt_pubs BY kegs DESCENDING pub_id ASCENDING. + DATA(lv_from) = iv_count + 1. + IF lines( rt_pubs ) >= lv_from. + DELETE rt_pubs FROM lv_from. + ENDIF. + ENDMETHOD. + + METHOD add_entries. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(ls_norm) = VALUE ty_entry( pub_id = to_upper( ls_entry-pub_id ) + keg_type = to_upper( ls_entry-keg_type ) + kegs = ls_entry-kegs ). + ASSIGN ct_agg[ pub_id = ls_norm-pub_id + keg_type = ls_norm-keg_type ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_norm-kegs. + ELSE. + INSERT ls_norm INTO TABLE ct_agg. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD add_pubs. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(lv_pub_id) = to_upper( ls_entry-pub_id ). + ASSIGN ct_pub[ pub_id = lv_pub_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs - ls_entry-kegs. + ELSE. + DATA(ls_new) = VALUE ty_pub( pub_id = lv_pub_id kegs = ls_entry-kegs ). + INSERT ls_new INTO TABLE ct_pub. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/generation.json b/tasks_gen/train/G1089/generation.json new file mode 100644 index 0000000..335fc4d --- /dev/null +++ b/tasks_gen/train/G1089/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1089", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1089/hidden/keg_hidden.clas.abap b/tasks_gen/train/G1089/hidden/keg_hidden.clas.abap new file mode 100644 index 0000000..9b988b3 --- /dev/null +++ b/tasks_gen/train/G1089/hidden/keg_hidden.clas.abap @@ -0,0 +1,162 @@ +CLASS {{p}}keg_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}keg_tour_merger. + METHODS setup. + METHODS merge_two_lists FOR TESTING. + METHODS merge_same_key FOR TESTING. + METHODS merge_ignores_zero FOR TESTING. + METHODS merge_ignores_negative FOR TESTING. + METHODS merge_upper_case FOR TESTING. + METHODS merge_is_sorted FOR TESTING. + METHODS merge_empty_lists FOR TESTING. + METHODS top_ranks_pubs FOR TESTING. + METHODS top_limits_rows FOR TESTING. + METHODS top_zero_count FOR TESTING. + METHODS top_ignores_invalid FOR TESTING. +ENDCLASS. + + +CLASS {{p}}keg_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}keg_tour_merger( ). + ENDMETHOD. + + METHOD merge_two_lists. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 4 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'PIL' kegs = 6 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) + ( pub_id = 'P01' keg_type = 'PIL' kegs = 6 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 4 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD merge_same_key. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 2 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 17 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD merge_ignores_zero. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 0 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 5 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P02' keg_type = 'LAG' kegs = 0 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P02' keg_type = 'LAG' kegs = 5 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD merge_ignores_negative. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = -3 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 5 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P02' keg_type = 'LAG' kegs = -1 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P02' keg_type = 'LAG' kegs = 5 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD merge_upper_case. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'p01' keg_type = 'lag' kegs = 3 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 8 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD merge_is_sorted. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P03' keg_type = 'LAG' kegs = 1 ) + ( pub_id = 'P01' keg_type = 'PIL' kegs = 2 ) + ( pub_id = 'P01' keg_type = 'LAG' kegs = 3 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 3 ) + ( pub_id = 'P01' keg_type = 'PIL' kegs = 2 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 1 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a + it_tour_b = VALUE #( ) ) ). + ENDMETHOD. + + METHOD merge_empty_lists. + DATA(lt_act) = mo_cut->merge_tours( it_tour_a = VALUE #( ) + it_tour_b = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ). + ENDMETHOD. + + METHOD top_ranks_pubs. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) + ( pub_id = 'P01' keg_type = 'PIL' kegs = 4 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 9 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 12 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_pub( + ( pub_id = 'P03' kegs = 12 ) + ( pub_id = 'P01' kegs = 9 ) + ( pub_id = 'P02' kegs = 9 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->top_pubs( it_tour = lt_tour iv_count = 3 ) ). + ENDMETHOD. + + METHOD top_limits_rows. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) + ( pub_id = 'P01' keg_type = 'PIL' kegs = 4 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 9 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 12 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_pub( + ( pub_id = 'P03' kegs = 12 ) + ( pub_id = 'P01' kegs = 9 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->top_pubs( it_tour = lt_tour iv_count = 2 ) ). + ENDMETHOD. + + METHOD top_zero_count. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) ). + DATA(lt_zero) = mo_cut->top_pubs( it_tour = lt_tour iv_count = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_zero ) ). + DATA(lt_neg) = mo_cut->top_pubs( it_tour = lt_tour iv_count = -1 ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_neg ) ). + ENDMETHOD. + + METHOD top_ignores_invalid. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 0 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = -4 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 6 ) + ( pub_id = 'p03' keg_type = 'PIL' kegs = 4 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_pub( + ( pub_id = 'P03' kegs = 10 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->top_pubs( it_tour = lt_tour iv_count = 5 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/mutation.json b/tasks_gen/train/G1089/mutation.json new file mode 100644 index 0000000..2765491 --- /dev/null +++ b/tasks_gen/train/G1089/mutation.json @@ -0,0 +1,62 @@ +{ + "task": "G1089", + "mutants": [ + { + "object": "{{P}}KEG_TOUR_MERGER", + "mutant": "line 50: < -> >= (rel)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}KEG_TOUR_MERGER", + "mutant": "line 65: < -> >= (rel)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "MERGE_IGNORES_NEGATIVE", + "MERGE_IGNORES_ZERO", + "MERGE_IS_SORTED", + "MERGE_SAME_KEY", + "MERGE_TWO_LISTS", + "MERGE_UPPER_CASE" + ] + }, + { + "object": "{{P}}KEG_TOUR_MERGER", + "mutant": "line 74: + -> - (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "MERGE_SAME_KEY", + "MERGE_UPPER_CASE" + ] + }, + { + "object": "{{P}}KEG_TOUR_MERGER", + "mutant": "line 83: < -> >= (rel)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "TOP_IGNORES_INVALID", + "TOP_LIMITS_ROWS", + "TOP_RANKS_PUBS" + ] + }, + { + "object": "{{P}}KEG_TOUR_MERGER", + "mutant": "line 89: + -> - (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "TOP_IGNORES_INVALID", + "TOP_LIMITS_ROWS", + "TOP_RANKS_PUBS" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1089/reference/keg_tour_merger.clas.abap b/tasks_gen/train/G1089/reference/keg_tour_merger.clas.abap new file mode 100644 index 0000000..9c5fef9 --- /dev/null +++ b/tasks_gen/train/G1089/reference/keg_tour_merger.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}keg_tour_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_entry, + pub_id TYPE c LENGTH 8, + keg_type TYPE c LENGTH 4, + kegs TYPE i, + END OF ty_entry, + tt_entry TYPE STANDARD TABLE OF ty_entry WITH EMPTY KEY, + BEGIN OF ty_pub, + pub_id TYPE c LENGTH 8, + kegs TYPE i, + END OF ty_pub, + tt_pub TYPE STANDARD TABLE OF ty_pub WITH EMPTY KEY. + + METHODS merge_tours + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + + METHODS top_pubs + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. + + PRIVATE SECTION. + TYPES tt_agg TYPE HASHED TABLE OF ty_entry WITH UNIQUE KEY pub_id keg_type. + TYPES tt_pub_agg TYPE HASHED TABLE OF ty_pub WITH UNIQUE KEY pub_id. + + METHODS add_entries + IMPORTING it_tour TYPE tt_entry + CHANGING ct_agg TYPE tt_agg. + + METHODS add_pubs + IMPORTING it_tour TYPE tt_entry + CHANGING ct_pub TYPE tt_pub_agg. +ENDCLASS. + + +CLASS {{p}}keg_tour_merger IMPLEMENTATION. + METHOD merge_tours. + DATA lt_agg TYPE tt_agg. + add_entries( EXPORTING it_tour = it_tour_a CHANGING ct_agg = lt_agg ). + add_entries( EXPORTING it_tour = it_tour_b CHANGING ct_agg = lt_agg ). + rt_tour = lt_agg. + SORT rt_tour BY pub_id keg_type. + ENDMETHOD. + + METHOD top_pubs. + IF iv_count < 1. + RETURN. + ENDIF. + DATA lt_agg TYPE tt_pub_agg. + add_pubs( EXPORTING it_tour = it_tour CHANGING ct_pub = lt_agg ). + rt_pubs = lt_agg. + SORT rt_pubs BY kegs DESCENDING pub_id ASCENDING. + DATA(lv_from) = iv_count + 1. + IF lines( rt_pubs ) >= lv_from. + DELETE rt_pubs FROM lv_from. + ENDIF. + ENDMETHOD. + + METHOD add_entries. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(ls_norm) = VALUE ty_entry( pub_id = to_upper( ls_entry-pub_id ) + keg_type = to_upper( ls_entry-keg_type ) + kegs = ls_entry-kegs ). + ASSIGN ct_agg[ pub_id = ls_norm-pub_id + keg_type = ls_norm-keg_type ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_norm-kegs. + ELSE. + INSERT ls_norm INTO TABLE ct_agg. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD add_pubs. + LOOP AT it_tour INTO DATA(ls_entry). + IF ls_entry-kegs < 1. + CONTINUE. + ENDIF. + DATA(lv_pub_id) = to_upper( ls_entry-pub_id ). + ASSIGN ct_pub[ pub_id = lv_pub_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -kegs = -kegs + ls_entry-kegs. + ELSE. + DATA(ls_new) = VALUE ty_pub( pub_id = lv_pub_id kegs = ls_entry-kegs ). + INSERT ls_new INTO TABLE ct_pub. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/reference/keg_tour_merger.testclasses.abap b/tasks_gen/train/G1089/reference/keg_tour_merger.testclasses.abap new file mode 100644 index 0000000..08c7509 --- /dev/null +++ b/tasks_gen/train/G1089/reference/keg_tour_merger.testclasses.abap @@ -0,0 +1,87 @@ +CLASS ltc_keg_tour_merger DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}keg_tour_merger. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS adds_same_key FOR TESTING. + METHODS ignores_invalid_kegs FOR TESTING. + METHODS returns_empty_list FOR TESTING. + METHODS ranks_pubs FOR TESTING. + METHODS limits_ranked_pubs FOR TESTING. +ENDCLASS. + + +CLASS ltc_keg_tour_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}keg_tour_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P02' keg_type = 'PIL' kegs = 7 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) + ( pub_id = 'P02' keg_type = 'PIL' kegs = 7 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD adds_same_key. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'p01' keg_type = 'lag' kegs = 4 ) ). + DATA(lt_b) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 6 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 10 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a it_tour_b = lt_b ) ). + ENDMETHOD. + + METHOD ignores_invalid_kegs. + DATA(lt_a) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 0 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = -2 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 8 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P03' keg_type = 'LAG' kegs = 8 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->merge_tours( it_tour_a = lt_a + it_tour_b = VALUE #( ) ) ). + ENDMETHOD. + + METHOD returns_empty_list. + DATA(lt_act) = mo_cut->merge_tours( it_tour_a = VALUE #( ) + it_tour_b = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ). + ENDMETHOD. + + METHOD ranks_pubs. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 5 ) + ( pub_id = 'P03' keg_type = 'LAG' kegs = 11 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_pub( + ( pub_id = 'P03' kegs = 11 ) + ( pub_id = 'P01' kegs = 5 ) + ( pub_id = 'P02' kegs = 5 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->top_pubs( it_tour = lt_tour iv_count = 3 ) ). + ENDMETHOD. + + METHOD limits_ranked_pubs. + DATA(lt_tour) = VALUE {{p}}keg_tour_merger=>tt_entry( + ( pub_id = 'P01' keg_type = 'LAG' kegs = 5 ) + ( pub_id = 'P02' keg_type = 'LAG' kegs = 9 ) ). + DATA(lt_exp) = VALUE {{p}}keg_tour_merger=>tt_pub( + ( pub_id = 'P02' kegs = 9 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lt_exp + act = mo_cut->top_pubs( it_tour = lt_tour iv_count = 1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1089/spec.md b/tasks_gen/train/G1089/spec.md new file mode 100644 index 0000000..0c33da4 --- /dev/null +++ b/tasks_gen/train/G1089/spec.md @@ -0,0 +1,63 @@ +# 1. Goal +A brewery plans the delivery tours of its beer kegs. Two depots send their tour +lists for one day. Merge the two lists into one tour list. Also find the pubs +with the largest demand. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP. +- No other object is necessary. Create all objects that you need. + +# 4. Contract +- Create the class {{P}}KEG_TOUR_MERGER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- Public types: + - TY_ENTRY: PUB_ID (type c, length 8), KEG_TYPE (type c, length 4), KEGS (type i). + - TT_ENTRY: standard table of TY_ENTRY with empty key. + - TY_PUB: PUB_ID (type c, length 8), KEGS (type i). + - TT_PUB: standard table of TY_PUB with empty key. +- Public methods: + - MERGE_TOURS + IMPORTING it_tour_a TYPE tt_entry + it_tour_b TYPE tt_entry + RETURNING VALUE(rt_tour) TYPE tt_entry. + - TOP_PUBS + IMPORTING it_tour TYPE tt_entry + iv_count TYPE i + RETURNING VALUE(rt_pubs) TYPE tt_pub. +- Do not add other public methods. + +# 5. Business rules + +MERGE_TOURS +1. An entry with KEGS less than 1 is not valid. Ignore all not valid entries of + both lists. +2. Convert PUB_ID and KEG_TYPE of a valid entry to upper case. +3. Two valid entries with the same PUB_ID and the same KEG_TYPE are one delivery. + Add their KEGS. +4. Return one row for each combination of PUB_ID and KEG_TYPE. The row contains + the PUB_ID, the KEG_TYPE and the total KEGS. +5. Sort the result by PUB_ID ascending and then by KEG_TYPE ascending. +6. If no valid entry exists, return an empty table. + +TOP_PUBS +7. An entry with KEGS less than 1 is not valid. Ignore all not valid entries. +8. Convert PUB_ID of a valid entry to upper case. +9. Add the KEGS of all valid entries with the same PUB_ID. +10. Return one row for each PUB_ID with the total KEGS. +11. Sort the result by KEGS descending and then by PUB_ID ascending. +12. Return only the first IV_COUNT rows. If IV_COUNT is less than 1, return an + empty table. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: no database table, no CDS view, no report, no function module. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1089/task.json b/tasks_gen/train/G1089/task.json new file mode 100644 index 0000000..058e7c6 --- /dev/null +++ b/tasks_gen/train/G1089/task.json @@ -0,0 +1,47 @@ +{ + "id": "G1089", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}KEG_TOUR_MERGER" + } + ], + "out_of_scope": [ + "new database tables", + "new CDS views", + "new reports", + "new function modules" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}KEG_HIDDEN", + "file": "hidden/keg_hidden.clas.abap", + "description": "T41 hidden tests for the keg tour merger" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}KEG_TOUR_MERGER", + "file": "reference/keg_tour_merger.clas.abap", + "description": "Keg tour merger", + "testclasses_file": "reference/keg_tour_merger.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops", + "table_kind" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1090/faulty/m1_ski_lift_old.prog.abap b/tasks_gen/train/G1090/faulty/m1_ski_lift_old.prog.abap new file mode 100644 index 0000000..44123cd --- /dev/null +++ b/tasks_gen/train/G1090/faulty/m1_ski_lift_old.prog.abap @@ -0,0 +1,97 @@ +REPORT {{p}}ski_lift_old. + +DATA gv_lift TYPE {{p}}skiride-lift_id. + +PARAMETERS p_season TYPE c LENGTH 4 OBLIGATORY. +SELECT-OPTIONS s_lift FOR gv_lift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_season TYPE c LENGTH 4. + TYPES tt_lift TYPE RANGE OF {{p}}skiride-lift_id. + TYPES: BEGIN OF ty_line, + lift_id TYPE {{p}}skiride-lift_id, + rides TYPE i, + tickets TYPE i, + revenue TYPE {{p}}skiride-fare, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + METHODS collect + IMPORTING iv_season TYPE ty_season + it_lift TYPE tt_lift + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT lift_id, + COUNT( * ) AS rides, + SUM( tickets ) AS tickets, + SUM( tickets * fare ) AS revenue + FROM {{p}}skiride + WHERE season <> @iv_season + AND lift_id IN @it_lift + GROUP BY lift_id + ORDER BY revenue DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_season = p_season it_lift = s_lift[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_lift FOR TESTING. + METHODS lift_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SKIRIDE' ) ) ). + DATA lt_ride TYPE STANDARD TABLE OF {{p}}skiride WITH EMPTY KEY. + lt_ride = VALUE #( ( ride_id = 1 lift_id = 'NORD' season = '2026' tickets = 10 fare = 25 ) + ( ride_id = 2 lift_id = 'NORD' season = '2026' tickets = 6 fare = 25 ) + ( ride_id = 3 lift_id = 'SUED' season = '2026' tickets = 20 fare = 30 ) ). + go_env->insert_test_data( lt_ride ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_lift. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_season = '2026' it_lift = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 600 ) act = lt_lines[ 1 ]-revenue ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-rides ). + cl_abap_unit_assert=>assert_equals( exp = 16 act = lt_lines[ 2 ]-tickets ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 2 ]-revenue ). + ENDMETHOD. + + METHOD lift_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_season = '2026' it_lift = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORD' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 1 ]-revenue ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1090/faulty/m2_ski_lift_old.prog.abap b/tasks_gen/train/G1090/faulty/m2_ski_lift_old.prog.abap new file mode 100644 index 0000000..e01f3cc --- /dev/null +++ b/tasks_gen/train/G1090/faulty/m2_ski_lift_old.prog.abap @@ -0,0 +1,97 @@ +REPORT {{p}}ski_lift_old. + +DATA gv_lift TYPE {{p}}skiride-lift_id. + +PARAMETERS p_season TYPE c LENGTH 4 OBLIGATORY. +SELECT-OPTIONS s_lift FOR gv_lift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_season TYPE c LENGTH 4. + TYPES tt_lift TYPE RANGE OF {{p}}skiride-lift_id. + TYPES: BEGIN OF ty_line, + lift_id TYPE {{p}}skiride-lift_id, + rides TYPE i, + tickets TYPE i, + revenue TYPE {{p}}skiride-fare, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + METHODS collect + IMPORTING iv_season TYPE ty_season + it_lift TYPE tt_lift + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT lift_id, + COUNT( * ) AS rides, + SUM( tickets ) AS tickets, + SUM( tickets * fare ) AS revenue + FROM {{p}}skiride + WHERE season = @iv_season + OR lift_id IN @it_lift + GROUP BY lift_id + ORDER BY revenue DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_season = p_season it_lift = s_lift[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_lift FOR TESTING. + METHODS lift_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SKIRIDE' ) ) ). + DATA lt_ride TYPE STANDARD TABLE OF {{p}}skiride WITH EMPTY KEY. + lt_ride = VALUE #( ( ride_id = 1 lift_id = 'NORD' season = '2026' tickets = 10 fare = 25 ) + ( ride_id = 2 lift_id = 'NORD' season = '2026' tickets = 6 fare = 25 ) + ( ride_id = 3 lift_id = 'SUED' season = '2026' tickets = 20 fare = 30 ) ). + go_env->insert_test_data( lt_ride ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_lift. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_season = '2026' it_lift = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 600 ) act = lt_lines[ 1 ]-revenue ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-rides ). + cl_abap_unit_assert=>assert_equals( exp = 16 act = lt_lines[ 2 ]-tickets ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 2 ]-revenue ). + ENDMETHOD. + + METHOD lift_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_season = '2026' it_lift = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORD' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 1 ]-revenue ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1090/generation.json b/tasks_gen/train/G1090/generation.json new file mode 100644 index 0000000..f857509 --- /dev/null +++ b/tasks_gen/train/G1090/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1090", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 2, + "killed": 2, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1090/hidden/ski_hidden.clas.abap b/tasks_gen/train/G1090/hidden/ski_hidden.clas.abap new file mode 100644 index 0000000..5635506 --- /dev/null +++ b/tasks_gen/train/G1090/hidden/ski_hidden.clas.abap @@ -0,0 +1,97 @@ +CLASS {{p}}ski_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_season TYPE c LENGTH 4. + TYPES tt_lift TYPE RANGE OF {{p}}skiride-lift_id. + METHODS run IMPORTING iv_season TYPE ty_season + it_lift TYPE tt_lift OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_lifts FOR TESTING. + METHODS sorted_by_revenue FOR TESTING. + METHODS totals_per_lift FOR TESTING. + METHODS season_filter FOR TESTING. + METHODS lift_filter FOR TESTING. + METHODS multi_lift_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ski_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}ski_lift_old WITH p_season = iv_season WITH s_lift IN it_lift AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Row { iv_row } missing| ). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_lifts. + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( run( '2026' ) ) ). + ENDMETHOD. + + METHOD sorted_by_revenue. + DATA(lr_data) = run( '2026' ). + cl_abap_unit_assert=>assert_equals( exp = `SUED` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LIFT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `NORD` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LIFT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OST` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LIFT_ID` ) ). + ENDMETHOD. + + METHOD totals_per_lift. + DATA(lr_data) = run( '2026' ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `RIDES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `25` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TICKETS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `750.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `RIDES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `16` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TICKETS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `400.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REVENUE` ) ). + ENDMETHOD. + + METHOD season_filter. + DATA(lr_data) = run( '2025' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `WEST` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LIFT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `4000.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + ENDMETHOD. + + METHOD lift_filter. + DATA(lr_data) = run( iv_season = '2026' + it_lift = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORD' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `RIDES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `400.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + ENDMETHOD. + + METHOD multi_lift_filter. + DATA(lr_data) = run( iv_season = '2026' + it_lift = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORD' ) + ( sign = 'I' option = 'EQ' low = 'OST' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `NORD` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LIFT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OST` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LIFT_ID` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1090/mutation.json b/tasks_gen/train/G1090/mutation.json new file mode 100644 index 0000000..e1c7e38 --- /dev/null +++ b/tasks_gen/train/G1090/mutation.json @@ -0,0 +1,44 @@ +{ + "task": "G1090", + "mutants": [ + { + "object": "{{P}}SKI_LIFT_OLD", + "mutant": "line 32: * -> / (arith)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}SKI_LIFT_OLD", + "mutant": "line 34: = -> <> (eq)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_LIFTS", + "LIFT_FILTER", + "MULTI_LIFT_FILTER", + "SEASON_FILTER", + "SORTED_BY_REVENUE", + "TOTALS_PER_LIFT" + ] + }, + { + "object": "{{P}}SKI_LIFT_OLD", + "mutant": "line 35: AND -> OR (logic)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_LIFTS", + "LIFT_FILTER", + "MULTI_LIFT_FILTER", + "SEASON_FILTER", + "SORTED_BY_REVENUE", + "TOTALS_PER_LIFT" + ] + } + ], + "valid": 2, + "killed": 2, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1090/reference/ski_lift_old.prog.abap b/tasks_gen/train/G1090/reference/ski_lift_old.prog.abap new file mode 100644 index 0000000..fdb9008 --- /dev/null +++ b/tasks_gen/train/G1090/reference/ski_lift_old.prog.abap @@ -0,0 +1,97 @@ +REPORT {{p}}ski_lift_old. + +DATA gv_lift TYPE {{p}}skiride-lift_id. + +PARAMETERS p_season TYPE c LENGTH 4 OBLIGATORY. +SELECT-OPTIONS s_lift FOR gv_lift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_season TYPE c LENGTH 4. + TYPES tt_lift TYPE RANGE OF {{p}}skiride-lift_id. + TYPES: BEGIN OF ty_line, + lift_id TYPE {{p}}skiride-lift_id, + rides TYPE i, + tickets TYPE i, + revenue TYPE {{p}}skiride-fare, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + METHODS collect + IMPORTING iv_season TYPE ty_season + it_lift TYPE tt_lift + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT lift_id, + COUNT( * ) AS rides, + SUM( tickets ) AS tickets, + SUM( tickets * fare ) AS revenue + FROM {{p}}skiride + WHERE season = @iv_season + AND lift_id IN @it_lift + GROUP BY lift_id + ORDER BY revenue DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_season = p_season it_lift = s_lift[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_lift FOR TESTING. + METHODS lift_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SKIRIDE' ) ) ). + DATA lt_ride TYPE STANDARD TABLE OF {{p}}skiride WITH EMPTY KEY. + lt_ride = VALUE #( ( ride_id = 1 lift_id = 'NORD' season = '2026' tickets = 10 fare = 25 ) + ( ride_id = 2 lift_id = 'NORD' season = '2026' tickets = 6 fare = 25 ) + ( ride_id = 3 lift_id = 'SUED' season = '2026' tickets = 20 fare = 30 ) ). + go_env->insert_test_data( lt_ride ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_lift. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_season = '2026' it_lift = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 600 ) act = lt_lines[ 1 ]-revenue ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-rides ). + cl_abap_unit_assert=>assert_equals( exp = 16 act = lt_lines[ 2 ]-tickets ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 2 ]-revenue ). + ENDMETHOD. + + METHOD lift_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_season = '2026' it_lift = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORD' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = lt_lines[ 1 ]-lift_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}skiride-fare( 400 ) act = lt_lines[ 1 ]-revenue ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1090/seed/ski_lift_old.prog.abap b/tasks_gen/train/G1090/seed/ski_lift_old.prog.abap new file mode 100644 index 0000000..65d4f0d --- /dev/null +++ b/tasks_gen/train/G1090/seed/ski_lift_old.prog.abap @@ -0,0 +1,78 @@ +REPORT {{p}}ski_lift_old. + +DATA: gv_lift TYPE {{p}}skiride-lift_id. + +PARAMETERS p_season TYPE c LENGTH 4 OBLIGATORY. +SELECT-OPTIONS s_lift FOR gv_lift. + +TYPES: BEGIN OF ty_line, + lift_id TYPE {{p}}skiride-lift_id, + rides TYPE i, + tickets TYPE i, + revenue TYPE p LENGTH 15 DECIMALS 2, + END OF ty_line. + +DATA: gt_rides TYPE STANDARD TABLE OF {{p}}skiride, + gt_lines TYPE STANDARD TABLE OF ty_line, + gs_ride TYPE {{p}}skiride, + gs_line TYPE ty_line, + gv_index TYPE i, + gv_found TYPE c LENGTH 1, + gv_rev TYPE p LENGTH 15 DECIMALS 2. + +START-OF-SELECTION. + PERFORM read_rides. + PERFORM build_lines. + PERFORM sort_lines. + PERFORM show_lines. + +FORM read_rides. + SELECT * FROM {{p}}skiride INTO TABLE gt_rides + WHERE season = p_season + AND lift_id IN s_lift. +ENDFORM. + +FORM build_lines. + LOOP AT gt_rides INTO gs_ride. + CLEAR: gv_found, gs_line. + LOOP AT gt_lines INTO gs_line. + IF gs_line-lift_id = gs_ride-lift_id. + gv_found = 'X'. + gv_index = sy-tabix. + EXIT. + ENDIF. + ENDLOOP. + IF gv_found = 'X'. + READ TABLE gt_lines INTO gs_line INDEX gv_index. + gs_line-rides = gs_line-rides + 1. + gs_line-tickets = gs_line-tickets + gs_ride-tickets. + COMPUTE gv_rev = gs_ride-tickets * gs_ride-fare. + gs_line-revenue = gs_line-revenue + gv_rev. + MODIFY gt_lines FROM gs_line INDEX gv_index. + ELSE. + CLEAR gs_line. + gs_line-lift_id = gs_ride-lift_id. + gs_line-rides = 1. + gs_line-tickets = gs_ride-tickets. + COMPUTE gs_line-revenue = gs_ride-tickets * gs_ride-fare. + APPEND gs_line TO gt_lines. + ENDIF. + ENDLOOP. +ENDFORM. + +FORM sort_lines. + SORT gt_lines BY revenue DESCENDING. +ENDFORM. + +FORM show_lines. + DATA: lo_alv TYPE REF TO cl_salv_table, + lx_msg TYPE REF TO cx_salv_msg. + TRY. + CALL METHOD cl_salv_table=>factory + IMPORTING r_salv_table = lo_alv + CHANGING t_table = gt_lines. + CALL METHOD lo_alv->display. + CATCH cx_salv_msg INTO lx_msg. + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. +ENDFORM. diff --git a/tasks_gen/train/G1090/seed/ski_seed.clas.abap b/tasks_gen/train/G1090/seed/ski_seed.clas.abap new file mode 100644 index 0000000..dabd400 --- /dev/null +++ b/tasks_gen/train/G1090/seed/ski_seed.clas.abap @@ -0,0 +1,18 @@ +CLASS {{p}}ski_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}ski_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}skiride. + INSERT {{p}}skiride FROM TABLE @( VALUE #( + ( ride_id = 1 lift_id = 'NORD' season = '2026' tickets = 10 fare = '25.00' ) + ( ride_id = 2 lift_id = 'NORD' season = '2026' tickets = 6 fare = '25.00' ) + ( ride_id = 3 lift_id = 'SUED' season = '2026' tickets = 20 fare = '30.00' ) + ( ride_id = 4 lift_id = 'SUED' season = '2026' tickets = 5 fare = '30.00' ) + ( ride_id = 5 lift_id = 'OST' season = '2026' tickets = 3 fare = '20.00' ) + ( ride_id = 6 lift_id = 'WEST' season = '2025' tickets = 100 fare = '40.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1090/seed/skiride.tabl.asabap b/tasks_gen/train/G1090/seed/skiride.tabl.asabap new file mode 100644 index 0000000..93b6496 --- /dev/null +++ b/tasks_gen/train/G1090/seed/skiride.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Rides of the ski lifts' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}skiride { + key client : abap.clnt not null; + key ride_id : abap.int4 not null; + lift_id : abap.char(4); + season : abap.char(4); + tickets : abap.int4; + fare : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1090/spec.md b/tasks_gen/train/G1090/spec.md new file mode 100644 index 0000000..9b58b5d --- /dev/null +++ b/tasks_gen/train/G1090/spec.md @@ -0,0 +1,48 @@ +# 1. Goal +Refactor the legacy report {{P}}SKI_LIFT_OLD in package $TMP. The report shows +the rides and the revenue of each lift of a ski resort for one season. The +operations team uses the report to compare the lifts. The behavior of the +report stays the same. + +# 2. Open questions +None. + +# 3. Context +- The executable program {{P}}SKI_LIFT_OLD exists in package $TMP. It is + active. The legacy code of this program is the specification of its + behavior. +- The table {{P}}SKIRIDE exists in package $TMP. It contains data. + Fields: RIDE_ID (INT4, key), LIFT_ID (CHAR 4), SEASON (CHAR 4), + TICKETS (INT4), FARE (DEC 15,2). +- One row of {{P}}SKIRIDE is one ride of one lift. FARE is the price of one + ticket of this ride. + +# 4. Contract +- Refactor the executable program {{P}}SKI_LIFT_OLD in package $TMP. The name + stays the same. +- The selection screen stays the same: + - Parameter P_SEASON, type CHAR 4, mandatory. + - Select-option S_LIFT for the lift. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: LIFT_ID, RIDES, TICKETS, REVENUE. + +# 5. Business rules +1. The behavior of the report stays the same. The legacy code in + {{P}}SKI_LIFT_OLD is the specification. Read the legacy code. Do not guess. +2. The selection screen, the filter, the totals and the sort order do not + change. +3. The report must stay fast when the table {{P}}SKIRIDE grows. Do not read the + same data more than one time. +4. The code must be easy to maintain. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. No method has more than 40 statements. No + database access in a loop. +- Out of scope: do not change {{P}}SKIRIDE and {{P}}SKI_SEED. + +# 7. Acceptance +- The program is active and has no syntax error. +- The behavior is the same as the behavior of the legacy code. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1090/task.json b/tasks_gen/train/G1090/task.json new file mode 100644 index 0000000..2a964bb --- /dev/null +++ b/tasks_gen/train/G1090/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1090", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SKIRIDE", + "file": "seed/skiride.tabl.asabap", + "description": "Rides of the ski lifts" + }, + { + "type": "PROG", + "name": "{{P}}SKI_LIFT_OLD", + "file": "seed/ski_lift_old.prog.abap", + "description": "Legacy ski lift revenue report" + }, + { + "type": "CLAS", + "name": "{{P}}SKI_SEED", + "file": "seed/ski_seed.clas.abap", + "description": "Ski ride seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}SKI_LIFT_OLD", + "parameters": [ + "p_season", + "s_lift" + ] + } + ], + "out_of_scope": [ + "{{P}}SKIRIDE", + "{{P}}SKI_SEED" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SKI_HIDDEN", + "file": "hidden/ski_hidden.clas.abap", + "description": "Hidden tests for the ski lift report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}SKI_LIFT_OLD", + "file": "reference/ski_lift_old.prog.abap", + "description": "Refactored ski lift report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1091/faulty/m0_i_po_value.ddls.asddls b/tasks_gen/train/G1091/faulty/m0_i_po_value.ddls.asddls new file mode 100644 index 0000000..4ab277f --- /dev/null +++ b/tasks_gen/train/G1091/faulty/m0_i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + left outer join {{p}}po_item as item on item.po_no = head.po_no + and item.fiscal_year = head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/faulty/m1_i_po_value.ddls.asddls b/tasks_gen/train/G1091/faulty/m1_i_po_value.ddls.asddls new file mode 100644 index 0000000..8b21082 --- /dev/null +++ b/tasks_gen/train/G1091/faulty/m1_i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no <> head.po_no + and item.fiscal_year = head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/faulty/m2_i_po_value.ddls.asddls b/tasks_gen/train/G1091/faulty/m2_i_po_value.ddls.asddls new file mode 100644 index 0000000..5cde46f --- /dev/null +++ b/tasks_gen/train/G1091/faulty/m2_i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no = head.po_no + or item.fiscal_year = head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/faulty/m3_i_po_value.ddls.asddls b/tasks_gen/train/G1091/faulty/m3_i_po_value.ddls.asddls new file mode 100644 index 0000000..b298bac --- /dev/null +++ b/tasks_gen/train/G1091/faulty/m3_i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no = head.po_no + and item.fiscal_year <> head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/faulty/m4_i_po_value.ddls.asddls b/tasks_gen/train/G1091/faulty/m4_i_po_value.ddls.asddls new file mode 100644 index 0000000..bd78c77 --- /dev/null +++ b/tasks_gen/train/G1091/faulty/m4_i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no = head.po_no + and item.fiscal_year = head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + max( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/generation.json b/tasks_gen/train/G1091/generation.json new file mode 100644 index 0000000..b1e4da9 --- /dev/null +++ b/tasks_gen/train/G1091/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1091", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1091/hidden/tpo_hidden.clas.abap b/tasks_gen/train/G1091/hidden/tpo_hidden.clas.abap new file mode 100644 index 0000000..e2c0211 --- /dev/null +++ b/tasks_gen/train/G1091/hidden/tpo_hidden.clas.abap @@ -0,0 +1,95 @@ +CLASS {{p}}tpo_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS total_of_2023 FOR TESTING. + METHODS total_of_2024 FOR TESTING. + METHODS counts_not_mixed FOR TESTING. + METHODS order_without_items FOR TESTING. + METHODS supplier_shown FOR TESTING. + METHODS two_rows_same_no FOR TESTING. + METHODS decimals FOR TESTING. + METHODS year_in_result FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tpo_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PO_VALUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_head TYPE STANDARD TABLE OF {{p}}po_head WITH EMPTY KEY. + DATA lt_item TYPE STANDARD TABLE OF {{p}}po_item WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_head = VALUE #( ( po_no = '1000' fiscal_year = '2023' supplier = 'ALPHA' ) + ( po_no = '1000' fiscal_year = '2024' supplier = 'BETA' ) + ( po_no = '2000' fiscal_year = '2023' supplier = 'GAMMA' ) + ( po_no = '3000' fiscal_year = '2024' supplier = 'DELTA' ) ). + lt_item = VALUE #( ( po_no = '1000' fiscal_year = '2023' item_no = 1 quantity = 3 price = '10.00' ) + ( po_no = '1000' fiscal_year = '2023' item_no = 2 quantity = 2 price = '5.00' ) + ( po_no = '1000' fiscal_year = '2024' item_no = 1 quantity = 1 price = '7.50' ) + ( po_no = '3000' fiscal_year = '2024' item_no = 1 quantity = 4 price = '2.25' ) ). + go_env->insert_test_data( lt_head ). + go_env->insert_test_data( lt_item ). + ENDMETHOD. + + METHOD total_of_2023. + SELECT SINGLE total_value FROM {{p}}i_po_value + WHERE po_no = '1000' AND fiscal_year = '2023' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '40.00' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD total_of_2024. + SELECT SINGLE total_value FROM {{p}}i_po_value + WHERE po_no = '1000' AND fiscal_year = '2024' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '7.50' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD counts_not_mixed. + SELECT SINGLE item_count FROM {{p}}i_po_value + WHERE po_no = '1000' AND fiscal_year = '2023' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + SELECT SINGLE item_count FROM {{p}}i_po_value + WHERE po_no = '1000' AND fiscal_year = '2024' INTO @DATA(lv_count_2024). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count_2024 ). + ENDMETHOD. + + METHOD order_without_items. + SELECT COUNT(*) FROM {{p}}i_po_value INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. + + METHOD supplier_shown. + SELECT SINGLE supplier FROM {{p}}i_po_value + WHERE po_no = '1000' AND fiscal_year = '2024' INTO @DATA(lv_supplier). + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lv_supplier ). + ENDMETHOD. + + METHOD two_rows_same_no. + SELECT COUNT(*) FROM {{p}}i_po_value WHERE po_no = '1000' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD decimals. + SELECT SINGLE total_value FROM {{p}}i_po_value + WHERE po_no = '3000' AND fiscal_year = '2024' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '9.00' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD year_in_result. + SELECT SINGLE fiscal_year FROM {{p}}i_po_value + WHERE po_no = '1000' AND supplier = 'BETA' INTO @DATA(lv_year). + cl_abap_unit_assert=>assert_equals( exp = '2024' act = lv_year ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1091/mutation.json b/tasks_gen/train/G1091/mutation.json new file mode 100644 index 0000000..38cbfc1 --- /dev/null +++ b/tasks_gen/train/G1091/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1091", + "mutants": [ + { + "object": "{{P}}I_PO_VALUE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "7/8", + "failed_tests": [ + "ORDER_WITHOUT_ITEMS" + ] + }, + { + "object": "{{P}}I_PO_VALUE", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "COUNTS_NOT_MIXED", + "DECIMALS", + "TOTAL_OF_2023", + "TOTAL_OF_2024", + "TWO_ROWS_SAME_NO" + ] + }, + { + "object": "{{P}}I_PO_VALUE", + "mutant": "line 6: and -> or (logic)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "COUNTS_NOT_MIXED", + "DECIMALS", + "ORDER_WITHOUT_ITEMS", + "TOTAL_OF_2023", + "TOTAL_OF_2024" + ] + }, + { + "object": "{{P}}I_PO_VALUE", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "COUNTS_NOT_MIXED", + "DECIMALS", + "ORDER_WITHOUT_ITEMS", + "TOTAL_OF_2023", + "TOTAL_OF_2024" + ] + }, + { + "object": "{{P}}I_PO_VALUE", + "mutant": "line 12: sum( -> max( (agg)", + "status": "killed", + "hidden": "7/8", + "failed_tests": [ + "TOTAL_OF_2023" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1091/reference/i_po_value.ddls.asddls b/tasks_gen/train/G1091/reference/i_po_value.ddls.asddls new file mode 100644 index 0000000..c54e16f --- /dev/null +++ b/tasks_gen/train/G1091/reference/i_po_value.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no = head.po_no + and item.fiscal_year = head.fiscal_year +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/reference/tpo_test.clas.abap b/tasks_gen/train/G1091/reference/tpo_test.clas.abap new file mode 100644 index 0000000..3b32980 --- /dev/null +++ b/tasks_gen/train/G1091/reference/tpo_test.clas.abap @@ -0,0 +1,53 @@ +CLASS {{p}}tpo_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS same_number_two_years FOR TESTING. + METHODS single_item_orders FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tpo_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PO_VALUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_head TYPE STANDARD TABLE OF {{p}}po_head WITH EMPTY KEY. + DATA lt_item TYPE STANDARD TABLE OF {{p}}po_item WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_head = VALUE #( ( po_no = '5000' fiscal_year = '2022' supplier = 'SUP1' ) + ( po_no = '5000' fiscal_year = '2023' supplier = 'SUP2' ) ). + lt_item = VALUE #( ( po_no = '5000' fiscal_year = '2022' item_no = 1 quantity = 1 price = '1.00' ) + ( po_no = '5000' fiscal_year = '2023' item_no = 1 quantity = 2 price = '2.00' ) ). + go_env->insert_test_data( lt_head ). + go_env->insert_test_data( lt_item ). + ENDMETHOD. + + METHOD same_number_two_years. + SELECT SINGLE total_value FROM {{p}}i_po_value + WHERE po_no = '5000' AND fiscal_year = '2022' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) + act = CONV decfloat34( lv_total ) ). + SELECT SINGLE total_value FROM {{p}}i_po_value + WHERE po_no = '5000' AND fiscal_year = '2023' INTO @DATA(lv_total_2023). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.00' ) + act = CONV decfloat34( lv_total_2023 ) ). + ENDMETHOD. + + METHOD single_item_orders. + SELECT SINGLE item_count FROM {{p}}i_po_value + WHERE po_no = '5000' AND fiscal_year = '2022' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ). + SELECT SINGLE item_count FROM {{p}}i_po_value + WHERE po_no = '5000' AND fiscal_year = '2023' INTO @DATA(lv_count_2023). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count_2023 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1091/seed/i_po_value.ddls.asddls b/tasks_gen/train/G1091/seed/i_po_value.ddls.asddls new file mode 100644 index 0000000..858105c --- /dev/null +++ b/tasks_gen/train/G1091/seed/i_po_value.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Purchase order value' +define view entity {{P}}I_PO_VALUE + as select from {{p}}po_head as head + inner join {{p}}po_item as item on item.po_no = head.po_no +{ + key head.po_no as po_no, + key head.fiscal_year as fiscal_year, + head.supplier as supplier, + count( * ) as item_count, + sum( cast( item.quantity as abap.dec( 13, 0 ) ) * item.price ) as total_value +} +group by + head.po_no, + head.fiscal_year, + head.supplier diff --git a/tasks_gen/train/G1091/seed/po_head.tabl.asabap b/tasks_gen/train/G1091/seed/po_head.tabl.asabap new file mode 100644 index 0000000..2610899 --- /dev/null +++ b/tasks_gen/train/G1091/seed/po_head.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Purchase order header' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}po_head { + key client : abap.clnt not null; + key po_no : abap.char(10) not null; + key fiscal_year : abap.char(4) not null; + supplier : abap.char(10); + po_date : abap.dats; +} diff --git a/tasks_gen/train/G1091/seed/po_item.tabl.asabap b/tasks_gen/train/G1091/seed/po_item.tabl.asabap new file mode 100644 index 0000000..845aaff --- /dev/null +++ b/tasks_gen/train/G1091/seed/po_item.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Purchase order item' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}po_item { + key client : abap.clnt not null; + key po_no : abap.char(10) not null; + key fiscal_year : abap.char(4) not null; + key item_no : abap.int4 not null; + quantity : abap.int4; + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1091/spec.md b/tasks_gen/train/G1091/spec.md new file mode 100644 index 0000000..606c63b --- /dev/null +++ b/tasks_gen/train/G1091/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +The CDS view entity {{P}}I_PO_VALUE shows the value of each purchase order of +an electronics company. The view gives a wrong result. When two purchase orders +have the same order number in two different fiscal years, the view shows a +total that is too high and an item count that is too high for both orders. +Fix the view. Each row must use only the items of its own purchase order. + +# 2. Open questions +None. + +# 3. Context +- The CDS view entity {{P}}I_PO_VALUE is in package $TMP. It is active. It reads + the tables {{P}}PO_HEAD and {{P}}PO_ITEM. +- Table {{P}}PO_HEAD (purchase order header), package $TMP. + Key: PO_NO (CHAR 10), FISCAL_YEAR (CHAR 4). + Fields: SUPPLIER (CHAR 10), PO_DATE (DATS). +- Table {{P}}PO_ITEM (purchase order item), package $TMP. + Key: PO_NO (CHAR 10), FISCAL_YEAR (CHAR 4), ITEM_NO (INT4). + Fields: QUANTITY (INT4), PRICE (DEC 15,2, the price for one unit). +- An order number is unique only inside one fiscal year. Two purchase orders can + have the same PO_NO in two different fiscal years. +- Read the current source of the view before you change it. + +# 4. Contract +- Fix the CDS view entity {{P}}I_PO_VALUE in package $TMP. The name of the view + and the names of the elements do not change. +- Elements: PO_NO (key), FISCAL_YEAR (key), SUPPLIER, ITEM_COUNT, TOTAL_VALUE. +- The authorization check stays #NOT_REQUIRED. + +# 5. Business rules +1. A purchase order is identified by PO_NO together with FISCAL_YEAR. +2. A row uses only the items of its own purchase order. Items of another + purchase order with the same PO_NO must not change ITEM_COUNT and must not + change TOTAL_VALUE. +3. All other behavior of the view stays the same. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816, ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep the view simple. +- Out of scope: do not change the tables {{P}}PO_HEAD and {{P}}PO_ITEM. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1091/task.json b/tasks_gen/train/G1091/task.json new file mode 100644 index 0000000..f34a62a --- /dev/null +++ b/tasks_gen/train/G1091/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1091", + "category": "I", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}PO_HEAD", + "file": "seed/po_head.tabl.asabap", + "description": "Purchase order header" + }, + { + "type": "TABL", + "name": "{{P}}PO_ITEM", + "file": "seed/po_item.tabl.asabap", + "description": "Purchase order item" + }, + { + "type": "DDLS", + "name": "{{P}}I_PO_VALUE", + "file": "seed/i_po_value.ddls.asddls", + "description": "Faulty purchase order value view" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_PO_VALUE", + "fields": [ + "PO_NO", + "FISCAL_YEAR", + "SUPPLIER", + "ITEM_COUNT", + "TOTAL_VALUE" + ] + } + ], + "out_of_scope": [ + "{{P}}PO_HEAD", + "{{P}}PO_ITEM" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TPO_HIDDEN", + "file": "hidden/tpo_hidden.clas.abap", + "description": "Hidden tests for the fixed purchase order value view" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_PO_VALUE", + "file": "reference/i_po_value.ddls.asddls", + "description": "Fixed purchase order value view" + }, + { + "type": "CLAS", + "name": "{{P}}TPO_TEST", + "file": "reference/tpo_test.clas.abap", + "description": "Own tests for the fixed view" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1092/faulty/m0_i_route_load.ddls.asddls b/tasks_gen/train/G1092/faulty/m0_i_route_load.ddls.asddls new file mode 100644 index 0000000..b93f24c --- /dev/null +++ b/tasks_gen/train/G1092/faulty/m0_i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + left outer join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + sum( veh.fee ) as total_fee +} +group by + sail.route_id +having + sum( veh.weight_kg ) > 10000 diff --git a/tasks_gen/train/G1092/faulty/m1_i_route_load.ddls.asddls b/tasks_gen/train/G1092/faulty/m1_i_route_load.ddls.asddls new file mode 100644 index 0000000..1e8faa6 --- /dev/null +++ b/tasks_gen/train/G1092/faulty/m1_i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + inner join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + max( veh.fee ) as total_fee +} +group by + sail.route_id +having + sum( veh.weight_kg ) > 10000 diff --git a/tasks_gen/train/G1092/faulty/m2_i_route_load.ddls.asddls b/tasks_gen/train/G1092/faulty/m2_i_route_load.ddls.asddls new file mode 100644 index 0000000..d7929c0 --- /dev/null +++ b/tasks_gen/train/G1092/faulty/m2_i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + inner join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + sum( veh.fee ) as total_fee +} +group by + sail.route_id +having + max( veh.weight_kg ) > 10000 diff --git a/tasks_gen/train/G1092/faulty/m3_i_route_load.ddls.asddls b/tasks_gen/train/G1092/faulty/m3_i_route_load.ddls.asddls new file mode 100644 index 0000000..657febd --- /dev/null +++ b/tasks_gen/train/G1092/faulty/m3_i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + inner join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + sum( veh.fee ) as total_fee +} +group by + sail.route_id +having + sum( veh.weight_kg ) <= 10000 diff --git a/tasks_gen/train/G1092/faulty/m4_i_route_load.ddls.asddls b/tasks_gen/train/G1092/faulty/m4_i_route_load.ddls.asddls new file mode 100644 index 0000000..5a7024d --- /dev/null +++ b/tasks_gen/train/G1092/faulty/m4_i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + inner join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + sum( veh.fee ) as total_fee +} +group by + sail.route_id +having + sum( veh.weight_kg ) > 10001 diff --git a/tasks_gen/train/G1092/generation.json b/tasks_gen/train/G1092/generation.json new file mode 100644 index 0000000..55cd26d --- /dev/null +++ b/tasks_gen/train/G1092/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1092", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 2, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/t16_hidden.clas.abap: name veh_count_ignores_empty_sailing has 31 characters (ABAP max 30)" + ], + "by_check": { + "static": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1092/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1092/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..3d03961 --- /dev/null +++ b/tasks_gen/train/G1092/hidden/t16_hidden.clas.abap @@ -0,0 +1,125 @@ +CLASS {{p}}t16_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS route_above_limit FOR TESTING. + METHODS veh_count_skips_empty_sail FOR TESTING. + METHODS total_fee_sum FOR TESTING. + METHODS second_route_shown FOR TESTING. + METHODS total_not_max FOR TESTING. + METHODS fee_of_multi_sailing_route FOR TESTING. + METHODS just_above_limit FOR TESTING. + METHODS route_below_limit FOR TESTING. + METHODS boundary_not_shown FOR TESTING. + METHODS route_without_vehicles FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROUTE_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_sail TYPE STANDARD TABLE OF {{p}}sail WITH EMPTY KEY. + DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_sail = VALUE #( ( sail_id = 'S1' route_id = 'R1' ) + ( sail_id = 'S2' route_id = 'R1' ) + ( sail_id = 'S7' route_id = 'R1' ) + ( sail_id = 'S3' route_id = 'R2' ) + ( sail_id = 'S4' route_id = 'R3' ) + ( sail_id = 'S5' route_id = 'R4' ) + ( sail_id = 'S6' route_id = 'R5' ) + ( sail_id = 'S8' route_id = 'R6' ) + ( sail_id = 'S9' route_id = 'R6' ) + ( sail_id = 'S10' route_id = 'R7' ) ). + lt_veh = VALUE #( ( sail_id = 'S1' veh_no = 1 veh_type = 'CAR' weight_kg = 1500 fee = '50.00' ) + ( sail_id = 'S1' veh_no = 2 veh_type = 'TRUCK' weight_kg = 12000 fee = '200.00' ) + ( sail_id = 'S2' veh_no = 1 veh_type = 'CAR' weight_kg = 1800 fee = '60.00' ) + ( sail_id = 'S3' veh_no = 1 veh_type = 'CAR' weight_kg = 2000 fee = '55.00' ) + ( sail_id = 'S4' veh_no = 1 veh_type = 'CAR' weight_kg = 10000 fee = '90.00' ) + ( sail_id = 'S5' veh_no = 1 veh_type = 'CAR' weight_kg = 11000 fee = '100.00' ) + ( sail_id = 'S8' veh_no = 1 veh_type = 'CAR' weight_kg = 6000 fee = '40.00' ) + ( sail_id = 'S9' veh_no = 1 veh_type = 'CAR' weight_kg = 6000 fee = '60.00' ) + ( sail_id = 'S10' veh_no = 1 veh_type = 'CAR' weight_kg = 10001 fee = '80.00' ) ). + go_env->insert_test_data( lt_sail ). + go_env->insert_test_data( lt_veh ). + ENDMETHOD. + + METHOD route_above_limit. + SELECT SINGLE total_weight FROM {{p}}i_route_load WHERE route_id = 'R1' + INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15300 ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD veh_count_skips_empty_sail. + SELECT SINGLE veh_count FROM {{p}}i_route_load WHERE route_id = 'R1' + INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD total_fee_sum. + SELECT SINGLE total_fee FROM {{p}}i_route_load WHERE route_id = 'R1' + INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '310.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD second_route_shown. + SELECT SINGLE total_weight FROM {{p}}i_route_load WHERE route_id = 'R4' + INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 11000 ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD total_not_max. + SELECT SINGLE total_weight FROM {{p}}i_route_load WHERE route_id = 'R6' + INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 12000 ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD fee_of_multi_sailing_route. + SELECT SINGLE total_fee FROM {{p}}i_route_load WHERE route_id = 'R6' + INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '100.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD just_above_limit. + SELECT SINGLE total_weight FROM {{p}}i_route_load WHERE route_id = 'R7' + INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10001 ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD route_below_limit. + SELECT COUNT(*) FROM {{p}}i_route_load WHERE route_id = 'R2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD boundary_not_shown. + SELECT COUNT(*) FROM {{p}}i_route_load WHERE route_id = 'R3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD route_without_vehicles. + SELECT COUNT(*) FROM {{p}}i_route_load WHERE route_id = 'R5' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_route_load INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1092/mutation.json b/tasks_gen/train/G1092/mutation.json new file mode 100644 index 0000000..a4ad613 --- /dev/null +++ b/tasks_gen/train/G1092/mutation.json @@ -0,0 +1,67 @@ +{ + "task": "G1092", + "mutants": [ + { + "object": "{{P}}I_ROUTE_LOAD", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "VEH_COUNT_SKIPS_EMPTY_SAIL" + ] + }, + { + "object": "{{P}}I_ROUTE_LOAD", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "FEE_OF_MULTI_SAILING_ROUTE", + "TOTAL_FEE_SUM" + ] + }, + { + "object": "{{P}}I_ROUTE_LOAD", + "mutant": "line 15: sum( -> max( (agg)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "FEE_OF_MULTI_SAILING_ROUTE", + "ROW_COUNT", + "TOTAL_NOT_MAX" + ] + }, + { + "object": "{{P}}I_ROUTE_LOAD", + "mutant": "line 15: > -> <= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "BOUNDARY_NOT_SHOWN", + "FEE_OF_MULTI_SAILING_ROUTE", + "JUST_ABOVE_LIMIT", + "ROUTE_ABOVE_LIMIT", + "ROUTE_BELOW_LIMIT", + "ROW_COUNT", + "SECOND_ROUTE_SHOWN", + "TOTAL_FEE_SUM", + "TOTAL_NOT_MAX", + "VEH_COUNT_SKIPS_EMPTY_SAIL" + ] + }, + { + "object": "{{P}}I_ROUTE_LOAD", + "mutant": "line 15: 10000 -> 10001 (const)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "JUST_ABOVE_LIMIT", + "ROW_COUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1092/reference/i_route_load.ddls.asddls b/tasks_gen/train/G1092/reference/i_route_load.ddls.asddls new file mode 100644 index 0000000..cbd5372 --- /dev/null +++ b/tasks_gen/train/G1092/reference/i_route_load.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Ferry route load' +define view entity {{P}}I_ROUTE_LOAD + as select from {{p}}sail as sail + inner join {{p}}veh as veh on veh.sail_id = sail.sail_id +{ + key sail.route_id as route_id, + count( * ) as veh_count, + sum( veh.weight_kg ) as total_weight, + sum( veh.fee ) as total_fee +} +group by + sail.route_id +having + sum( veh.weight_kg ) > 10000 diff --git a/tasks_gen/train/G1092/reference/t16_test.clas.abap b/tasks_gen/train/G1092/reference/t16_test.clas.abap new file mode 100644 index 0000000..8301282 --- /dev/null +++ b/tasks_gen/train/G1092/reference/t16_test.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}t16_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS load_of_one_route FOR TESTING. + METHODS light_route_hidden FOR TESTING. + METHODS just_above_limit_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROUTE_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_sail TYPE STANDARD TABLE OF {{p}}sail WITH EMPTY KEY. + DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_sail = VALUE #( ( sail_id = 'A1' route_id = 'NORTH' ) + ( sail_id = 'A2' route_id = 'SOUTH' ) + ( sail_id = 'A3' route_id = 'EDGE' ) ). + lt_veh = VALUE #( ( sail_id = 'A1' veh_no = 1 veh_type = 'CAR' weight_kg = 8000 fee = '40.00' ) + ( sail_id = 'A1' veh_no = 2 veh_type = 'TRUCK' weight_kg = 5000 fee = '75.00' ) + ( sail_id = 'A2' veh_no = 1 veh_type = 'CAR' weight_kg = 1200 fee = '20.00' ) + ( sail_id = 'A3' veh_no = 1 veh_type = 'CAR' weight_kg = 10001 fee = '80.00' ) ). + go_env->insert_test_data( lt_sail ). + go_env->insert_test_data( lt_veh ). + ENDMETHOD. + + METHOD load_of_one_route. + SELECT SINGLE veh_count, total_weight, total_fee FROM {{p}}i_route_load + WHERE route_id = 'NORTH' INTO @DATA(ls_load). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_load-veh_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 13000 ) + act = CONV decfloat34( ls_load-total_weight ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '115.00' ) + act = CONV decfloat34( ls_load-total_fee ) ). + ENDMETHOD. + + METHOD light_route_hidden. + SELECT COUNT(*) FROM {{p}}i_route_load WHERE route_id = 'SOUTH' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD just_above_limit_shown. + SELECT SINGLE total_weight FROM {{p}}i_route_load WHERE route_id = 'EDGE' + INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10001 ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1092/seed/sail.tabl.asabap b/tasks_gen/train/G1092/seed/sail.tabl.asabap new file mode 100644 index 0000000..0cf158a --- /dev/null +++ b/tasks_gen/train/G1092/seed/sail.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Ferry sailing' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}sail { + key client : abap.clnt not null; + key sail_id : abap.char(10) not null; + route_id : abap.char(10); + sail_date : abap.dats; +} diff --git a/tasks_gen/train/G1092/seed/veh.tabl.asabap b/tasks_gen/train/G1092/seed/veh.tabl.asabap new file mode 100644 index 0000000..001b335 --- /dev/null +++ b/tasks_gen/train/G1092/seed/veh.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Vehicle on board' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}veh { + key client : abap.clnt not null; + key sail_id : abap.char(10) not null; + key veh_no : abap.int4 not null; + veh_type : abap.char(4); + weight_kg : abap.int4; + fee : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1092/spec.md b/tasks_gen/train/G1092/spec.md new file mode 100644 index 0000000..2f75408 --- /dev/null +++ b/tasks_gen/train/G1092/spec.md @@ -0,0 +1,41 @@ +# 1. Goal +A ferry operator wants to know the load of each of its routes. A report and an +OData service will read the load. The calculation must run in the database, in a +CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}SAIL (ferry sailing), package $TMP. Key: SAIL_ID (CHAR 10). + Fields: ROUTE_ID (CHAR 10), SAIL_DATE (DATS). +- Table {{P}}VEH (vehicle on board), package $TMP. Key: SAIL_ID (CHAR 10) and + VEH_NO (INT4). Fields: VEH_TYPE (CHAR 4), WEIGHT_KG (INT4), + FEE (DEC 15,2, the fee that the vehicle pays for the crossing). +- A route has one or more sailings. A sailing has zero or more vehicles. + +# 4. Contract +- Create the CDS view entity {{P}}I_ROUTE_LOAD in package $TMP. +- Elements, with these names: ROUTE_ID (key), VEH_COUNT, TOTAL_WEIGHT, TOTAL_FEE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each route that has at least one vehicle on one of its + sailings. Do not show a route without vehicles. +2. VEH_COUNT is the number of vehicles on all sailings of the route. A sailing + without vehicles does not add to VEH_COUNT. +3. TOTAL_WEIGHT is the sum of WEIGHT_KG of these vehicles, in kilograms. +4. TOTAL_FEE is the sum of FEE of these vehicles, with 2 decimals. +5. Show only the routes whose total weight is more than 10,000 kg. A route whose + total weight is 10,000 kg or less must not appear. A route whose total weight + is 10,001 kg must appear. + +# 6. Constraints +- Release target: 8.16 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep methods below 40 statements. +- Out of scope: do not change {{P}}SAIL and {{P}}VEH. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1092/task.json b/tasks_gen/train/G1092/task.json new file mode 100644 index 0000000..4c7d5d2 --- /dev/null +++ b/tasks_gen/train/G1092/task.json @@ -0,0 +1,65 @@ +{ + "id": "G1092", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SAIL", + "file": "seed/sail.tabl.asabap", + "description": "Ferry sailing" + }, + { + "type": "TABL", + "name": "{{P}}VEH", + "file": "seed/veh.tabl.asabap", + "description": "Vehicle on board" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_ROUTE_LOAD", + "fields": [ + "ROUTE_ID", + "VEH_COUNT", + "TOTAL_WEIGHT", + "TOTAL_FEE" + ] + } + ], + "out_of_scope": [ + "{{P}}SAIL", + "{{P}}VEH" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_ROUTE_LOAD", + "file": "reference/i_route_load.ddls.asddls", + "description": "Ferry route load" + }, + { + "type": "CLAS", + "name": "{{P}}T16_TEST", + "file": "reference/t16_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1093/generation.json b/tasks_gen/train/G1093/generation.json new file mode 100644 index 0000000..517783d --- /dev/null +++ b/tasks_gen/train/G1093/generation.json @@ -0,0 +1,42 @@ +{ + "id": "G1093", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/mix_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/mix_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 10 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1093/hidden/mix_hidden.clas.abap b/tasks_gen/train/G1093/hidden/mix_hidden.clas.abap new file mode 100644 index 0000000..1a3d537 --- /dev/null +++ b/tasks_gen/train/G1093/hidden/mix_hidden.clas.abap @@ -0,0 +1,121 @@ +CLASS {{p}}mix_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING it_tints TYPE {{P}}PAINT_MIX=>TY_TINTS + EXPORTING ev_volume TYPE decfloat34 + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS empty_table FOR TESTING. + METHODS single_tint FOR TESTING. + METHODS duplicate_tints FOR TESTING. + METHODS two_tints FOR TESTING. + METHODS zero_quantity FOR TESTING. + METHODS negative_quantity FOR TESTING. + METHODS unknown_tint FOR TESTING. + METHODS rounding FOR TESTING. + METHODS quantity_checked_first FOR TESTING. + METHODS fee_per_distinct_tint FOR TESTING. +ENDCLASS. + + +CLASS {{p}}mix_hidden IMPLEMENTATION. + METHOD call. + CALL FUNCTION '{{P}}MIX_PRICE' + EXPORTING + it_tints = it_tints + IMPORTING + ev_volume = ev_volume + ev_price = ev_price + EXCEPTIONS + no_components = 1 + invalid_quantity = 2 + unknown_tint = 3 + OTHERS = 4. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD empty_table. + DATA lt_tints TYPE {{P}}PAINT_MIX=>TY_TINTS. + call( EXPORTING it_tints = lt_tints + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD single_tint. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 1000 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1000 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '21.5' ) act = lv_price ). + ENDMETHOD. + + METHOD duplicate_tints. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 1000 ) + ( tint_id = 'WHIT' quantity_ml = 500 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1500 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '31.5' ) act = lv_price ). + ENDMETHOD. + + METHOD two_tints. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 1000 ) + ( tint_id = 'BLUE' quantity_ml = 200 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1200 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 32 ) act = lv_price ). + ENDMETHOD. + + METHOD zero_quantity. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 0 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD negative_quantity. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'BLUE' quantity_ml = -5 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD unknown_tint. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'XXXX' quantity_ml = 100 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ). + ENDMETHOD. + + METHOD rounding. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'BLUE' quantity_ml = 111 ) ) + IMPORTING ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '6.5' ) act = lv_price ). + ENDMETHOD. + + METHOD quantity_checked_first. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'XXXX' quantity_ml = 100 ) + ( tint_id = 'WHIT' quantity_ml = 0 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD fee_per_distinct_tint. + call( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 100 ) + ( tint_id = 'WHIT' quantity_ml = 100 ) + ( tint_id = 'BLUE' quantity_ml = 100 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 300 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '11.5' ) act = lv_price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1093/mutation.json b/tasks_gen/train/G1093/mutation.json new file mode 100644 index 0000000..d76e86d --- /dev/null +++ b/tasks_gen/train/G1093/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "T_C_MIX", + "mutants": [ + { + "object": "{{P}}MIX_PRICE", + "mutant": "line 33: <= -> < (rel)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "ZERO_QUANTITY" + ] + }, + { + "object": "{{P}}MIX_PRICE", + "mutant": "line 53: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "SINGLE_TINT", + "DUPLICATE_TINTS", + "TWO_TINTS", + "FEE_PER_DISTINCT_TINT" + ] + }, + { + "object": "{{P}}MIX_PRICE", + "mutant": "line 53: * -> + (arith)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "SINGLE_TINT", + "DUPLICATE_TINTS", + "TWO_TINTS", + "ROUNDING", + "FEE_PER_DISTINCT_TINT" + ] + }, + { + "object": "{{P}}MIX_PRICE", + "mutant": "line 56: c_mix_fee 1.5 -> 1.6 (const)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "SINGLE_TINT", + "DUPLICATE_TINTS", + "TWO_TINTS", + "ROUNDING", + "FEE_PER_DISTINCT_TINT" + ] + }, + { + "object": "{{P}}MIX_PRICE", + "mutant": "line 57: round_half_up -> round_half_down (mode)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "ROUNDING" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1093/reference/mix_price.func.abap b/tasks_gen/train/G1093/reference/mix_price.func.abap new file mode 100644 index 0000000..65de6e0 --- /dev/null +++ b/tasks_gen/train/G1093/reference/mix_price.func.abap @@ -0,0 +1,57 @@ +FUNCTION {{P}}MIX_PRICE + IMPORTING + it_tints TYPE {{P}}PAINT_MIX=>TY_TINTS + EXPORTING + ev_volume TYPE decfloat34 + ev_price TYPE decfloat34 + EXCEPTIONS + no_components + invalid_quantity + unknown_tint. + + CONSTANTS c_mix_fee TYPE decfloat34 VALUE '1.5'. + + TYPES: ty_id TYPE c LENGTH 4, + ty_ids TYPE SORTED TABLE OF ty_id WITH UNIQUE KEY table_line, + BEGIN OF ty_price, + tint_id TYPE c LENGTH 4, + price_ml TYPE p LENGTH 13 DECIMALS 3, + END OF ty_price, + ty_prices TYPE SORTED TABLE OF ty_price WITH UNIQUE KEY tint_id. + + IF it_tints IS INITIAL. + RAISE no_components. + ENDIF. + + DATA lt_ids TYPE ty_ids. + LOOP AT it_tints INTO DATA(ls_tint). + IF ls_tint-quantity_ml <= 0. + RAISE invalid_quantity. + ENDIF. + INSERT VALUE #( table_line = ls_tint-tint_id ) INTO TABLE lt_ids. + ENDLOOP. + + DATA lt_prices TYPE ty_prices. + SELECT tint_id, price_ml FROM {{P}}TINTPR + WHERE tint_id IN @lt_ids + ORDER BY tint_id + INTO TABLE @lt_prices. + + LOOP AT lt_ids INTO DATA(lv_id). + READ TABLE lt_prices WITH TABLE KEY tint_id = lv_id TRANSPORTING NO FIELDS. + IF sy-subrc <> 0. + RAISE unknown_tint. + ENDIF. + ENDLOOP. + + DATA lv_material TYPE decfloat34. + LOOP AT it_tints INTO DATA(ls_item). + READ TABLE lt_prices INTO DATA(ls_price) WITH TABLE KEY tint_id = ls_item-tint_id. + ev_volume = ev_volume + ls_item-quantity_ml. + lv_material = lv_material + ls_item-quantity_ml * ls_price-price_ml. + ENDLOOP. + + ev_price = round( val = lv_material + lines( lt_ids ) * c_mix_fee + dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1093/reference/mix_test.clas.abap b/tasks_gen/train/G1093/reference/mix_test.clas.abap new file mode 100644 index 0000000..582e8a1 --- /dev/null +++ b/tasks_gen/train/G1093/reference/mix_test.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}mix_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_of + IMPORTING it_tints TYPE {{P}}PAINT_MIX=>TY_TINTS + EXPORTING ev_volume TYPE decfloat34 + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS two_tints FOR TESTING. + METHODS repeated_tint FOR TESTING. + METHODS empty_order FOR TESTING. + METHODS bad_quantity FOR TESTING. + METHODS unknown_tint FOR TESTING. +ENDCLASS. + + +CLASS {{p}}mix_test IMPLEMENTATION. + METHOD price_of. + CALL FUNCTION '{{P}}MIX_PRICE' + EXPORTING + it_tints = it_tints + IMPORTING + ev_volume = ev_volume + ev_price = ev_price + EXCEPTIONS + no_components = 1 + invalid_quantity = 2 + unknown_tint = 3 + OTHERS = 4. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD two_tints. + price_of( EXPORTING it_tints = VALUE #( ( tint_id = 'WHIT' quantity_ml = 1000 ) + ( tint_id = 'BLUE' quantity_ml = 200 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1200 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 32 ) act = lv_price ). + ENDMETHOD. + + METHOD repeated_tint. + price_of( EXPORTING it_tints = VALUE #( ( tint_id = 'GREY' quantity_ml = 200 ) + ( tint_id = 'GREY' quantity_ml = 300 ) ) + IMPORTING ev_volume = DATA(lv_volume) + ev_price = DATA(lv_price) + ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 500 ) act = lv_volume ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '16.5' ) act = lv_price ). + ENDMETHOD. + + METHOD empty_order. + DATA lt_tints TYPE {{P}}PAINT_MIX=>TY_TINTS. + price_of( EXPORTING it_tints = lt_tints + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD bad_quantity. + price_of( EXPORTING it_tints = VALUE #( ( tint_id = 'GOLD' quantity_ml = -1 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD unknown_tint. + price_of( EXPORTING it_tints = VALUE #( ( tint_id = 'ZZZZ' quantity_ml = 10 ) ) + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1093/seed/mix_sample.func.abap b/tasks_gen/train/G1093/seed/mix_sample.func.abap new file mode 100644 index 0000000..a412868 --- /dev/null +++ b/tasks_gen/train/G1093/seed/mix_sample.func.abap @@ -0,0 +1,17 @@ +FUNCTION {{P}}MIX_SAMPLE + IMPORTING + it_tints TYPE {{P}}PAINT_MIX=>TY_TINTS + EXPORTING + ev_volume TYPE decfloat34 + EXCEPTIONS + no_components. + + IF it_tints IS INITIAL. + RAISE no_components. + ENDIF. + + LOOP AT it_tints INTO DATA(ls_tint). + ev_volume = ev_volume + ls_tint-quantity_ml. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1093/seed/paint_mix.clas.abap b/tasks_gen/train/G1093/seed/paint_mix.clas.abap new file mode 100644 index 0000000..3733bbd --- /dev/null +++ b/tasks_gen/train/G1093/seed/paint_mix.clas.abap @@ -0,0 +1,28 @@ +CLASS {{p}}paint_mix DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_tint, + tint_id TYPE c LENGTH 4, + quantity_ml TYPE i, + END OF ty_tint, + ty_tints TYPE STANDARD TABLE OF ty_tint WITH EMPTY KEY. + + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}paint_mix IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_prices TYPE STANDARD TABLE OF {{p}}tintpr WITH EMPTY KEY. + lt_prices = VALUE #( + ( tint_id = 'WHIT' price_ml = '0.020' ) + ( tint_id = 'BLUE' price_ml = '0.045' ) + ( tint_id = 'GREY' price_ml = '0.030' ) + ( tint_id = 'GOLD' price_ml = '0.250' ) ). + + DELETE FROM {{p}}tintpr. + INSERT {{p}}tintpr FROM TABLE @lt_prices. + + out->write( |Tint prices seeded: { lines( lt_prices ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1093/seed/tintpr.tabl.abap b/tasks_gen/train/G1093/seed/tintpr.tabl.abap new file mode 100644 index 0000000..6043fbd --- /dev/null +++ b/tasks_gen/train/G1093/seed/tintpr.tabl.abap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Tint prices' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}tintpr { + key client : abap.clnt not null; + key tint_id : abap.char(4) not null; + price_ml : abap.dec(13,3) not null; +} diff --git a/tasks_gen/train/G1093/spec.md b/tasks_gen/train/G1093/spec.md new file mode 100644 index 0000000..feb10e9 --- /dev/null +++ b/tasks_gen/train/G1093/spec.md @@ -0,0 +1,73 @@ +# 1. Goal +A paint shop mixes custom paint colours. A customer order contains one or more +tint components. The checkout program calls one function module for each order. +The function module returns the total volume and the price of the mix. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}PAINT_MIX exists in package $TMP. It contains the public types + ty_tint and ty_tints. The checkout program and the function module use these + types. +- The table {{P}}TINTPR exists in package $TMP. It contains the price of one + millilitre of each tint. The class {{P}}PAINT_MIX fills this table. +- The function group {{P}}FG_PAINT exists in package $TMP. +- The function module {{P}}MIX_SAMPLE exists in this function group. It is a + sample. It shows the interface that the checkout program uses. + +# 4. Contract +- Create the function module {{P}}MIX_PRICE in function group {{P}}FG_PAINT. +- Use exactly this interface: + + IMPORTING + it_tints TYPE {{P}}PAINT_MIX=>TY_TINTS + EXPORTING + ev_volume TYPE decfloat34 + ev_price TYPE decfloat34 + EXCEPTIONS + no_components + invalid_quantity + unknown_tint + +- The type {{P}}PAINT_MIX=>ty_tint has these components: + tint_id type c, length 4 + quantity_ml type i +- The type {{P}}PAINT_MIX=>ty_tints is a table of ty_tint. +- The table {{P}}TINTPR has these fields: + tint_id type c, length 4 + price_ml type packed number, 13 digits, 3 decimals +- The table {{P}}TINTPR contains this data: + WHIT 0.020 + BLUE 0.045 + GREY 0.030 + GOLD 0.250 + +# 5. Business rules +1. The component quantity_ml is a volume in millilitres. +2. If it_tints is empty, raise the exception NO_COMPONENTS. +3. If a component has a quantity_ml of zero or less, raise the exception + INVALID_QUANTITY. +4. If a component has a tint_id that is not in the table {{P}}TINTPR, raise the + exception UNKNOWN_TINT. +5. Check rule 2 first. Then check rule 3 for all components. Then check rule 4 + for all components. Raise only the first exception that occurs. +6. ev_volume is the sum of the quantity_ml of all components. If the same + tint_id occurs more than once, all its quantities count. +7. The material price of a component is its quantity_ml multiplied by the + price_ml of its tint. +8. The mixing fee is 1.50 for each different tint_id in the order. A tint_id + that occurs more than once counts once. +9. ev_price is the sum of the material prices of all components plus the mixing + fee. Round ev_price half up to 2 decimals. + +# 6. Constraints +- Release target: 7.58 (the system runs SAP_BASIS 816). +- Use Clean ABAP. Keep each method below 40 statements. +- Out of scope: do not change the class {{P}}PAINT_MIX, the table {{P}}TINTPR, + and the function module {{P}}MIX_SAMPLE. + +# 7. Acceptance +- The function module {{P}}MIX_PRICE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1093/task.json b/tasks_gen/train/G1093/task.json new file mode 100644 index 0000000..80c97c6 --- /dev/null +++ b/tasks_gen/train/G1093/task.json @@ -0,0 +1,91 @@ +{ + "id": "G1093", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}TINTPR", + "file": "seed/tintpr.tabl.abap", + "description": "Price of one millilitre of each tint" + }, + { + "type": "CLAS", + "name": "{{P}}PAINT_MIX", + "file": "seed/paint_mix.clas.abap", + "description": "Component types and tint price seed data", + "run": true + }, + { + "type": "FUGR", + "name": "{{P}}FG_PAINT", + "description": "Paint mixing" + }, + { + "type": "FUNC", + "name": "{{P}}MIX_SAMPLE", + "functionGroup": "{{P}}FG_PAINT", + "file": "seed/mix_sample.func.abap", + "description": "Sample module that shows the interface" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}MIX_PRICE", + "functionGroup": "{{P}}FG_PAINT", + "params": [ + { + "name": "it_tints", + "type": "{{P}}PAINT_MIX=>TY_TINTS" + }, + { + "name": "ev_volume", + "type": "decfloat34" + }, + { + "name": "ev_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}PAINT_MIX", + "{{P}}TINTPR", + "{{P}}MIX_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}MIX_HIDDEN", + "file": "hidden/mix_hidden.clas.abap", + "description": "Hidden tests for the paint mix price" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}MIX_PRICE", + "functionGroup": "{{P}}FG_PAINT", + "file": "reference/mix_price.func.abap", + "description": "Paint mix price" + }, + { + "type": "CLAS", + "name": "{{P}}MIX_TEST", + "file": "reference/mix_test.clas.abap", + "description": "Own tests for the paint mix price" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1094/generation.json b/tasks_gen/train/G1094/generation.json new file mode 100644 index 0000000..c65f284 --- /dev/null +++ b/tasks_gen/train/G1094/generation.json @@ -0,0 +1,50 @@ +{ + "id": "G1094", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0141 (similarity spec 0.61, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0190 (similarity spec 0.60, rules 0.68, names 0.25). Choose a different business topic and different object names.", + "Too close to task G1090 (similarity spec 0.61, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "seed/lesson.tabl.asabap: field name minutes is a reserved word; choose another name", + "reference/lesson_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/lesson_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "reference/lesson_old.prog.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/lesson_old.prog.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"75\"", + "Too close to task G0190 (similarity spec 0.57, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "static": 1, + "abaplint": 4, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G0190 (similarity spec 0.58, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1094/hidden/court_hidden.clas.abap b/tasks_gen/train/G1094/hidden/court_hidden.clas.abap new file mode 100644 index 0000000..43e7d88 --- /dev/null +++ b/tasks_gen/train/G1094/hidden/court_hidden.clas.abap @@ -0,0 +1,125 @@ +CLASS {{p}}court_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_court TYPE RANGE OF {{p}}court-court_id. + METHODS run + IMPORTING iv_date TYPE d + it_court TYPE tt_court OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS row_of + IMPORTING ir_data TYPE REF TO data + iv_court TYPE string + RETURNING VALUE(rv_row) TYPE i. + METHODS busy_courts_only FOR TESTING. + METHODS quiet_courts_dropped FOR TESTING. + METHODS one_day_only FOR TESTING. + METHODS cancelled_ignored FOR TESTING. + METHODS totals_per_court FOR TESTING. + METHODS court_filter FOR TESTING. + METHODS alphabetical_order FOR TESTING. +ENDCLASS. + + +CLASS {{p}}court_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}court_old WITH p_date = iv_date WITH s_court IN it_court AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_value = condense( CONV string( ) ). + ENDIF. + ENDIF. + ENDIF. + ENDMETHOD. + + METHOD row_of. + DATA lv_lines TYPE i. + lv_lines = lines_of( ir_data ). + DO lv_lines TIMES. + IF value_of( ir_data = ir_data iv_row = sy-index iv_col = `COURT` ) = iv_court. + rv_row = sy-index. + RETURN. + ENDIF. + ENDDO. + ENDMETHOD. + + METHOD busy_courts_only. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( run( '20260401' ) ) ). + ENDMETHOD. + + METHOD quiet_courts_dropped. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = row_of( ir_data = lr_data iv_court = 'C003' ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = row_of( ir_data = lr_data iv_court = 'C004' ) ). + ENDMETHOD. + + METHOD one_day_only. + DATA(lr_data) = run( '20260331' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `C003` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOOKINGS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `120` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL_MIN` ) ). + cl_abap_unit_assert=>assert_equals( exp = `40.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD cancelled_ignored. + DATA(lr_data) = run( '20260401' ). + DATA(lv_row) = row_of( ir_data = lr_data iv_court = 'C002' ). + cl_abap_unit_assert=>assert_differs( exp = 0 act = lv_row msg = 'court C002 missing' ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `BOOKINGS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `120` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `TOTAL_MIN` ) ). + cl_abap_unit_assert=>assert_equals( exp = `32.00` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD totals_per_court. + DATA(lr_data) = run( '20260401' ). + DATA(lv_row) = row_of( ir_data = lr_data iv_court = 'C001' ). + cl_abap_unit_assert=>assert_differs( exp = 0 act = lv_row msg = 'court C001 missing' ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `BOOKINGS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `90` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `TOTAL_MIN` ) ). + cl_abap_unit_assert=>assert_equals( exp = `18.00` act = value_of( ir_data = lr_data iv_row = lv_row iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD court_filter. + DATA(lr_data) = run( iv_date = '20260401' + it_court = VALUE #( ( sign = 'I' option = 'EQ' low = 'C001' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `C001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `18.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD alphabetical_order. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `C001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `C002` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COURT` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1094/mutation.json b/tasks_gen/train/G1094/mutation.json new file mode 100644 index 0000000..b9eb49e --- /dev/null +++ b/tasks_gen/train/G1094/mutation.json @@ -0,0 +1,34 @@ +{ + "task": "T31", + "mutants": [ + { + "object": "{{P}}COURT_OLD", + "mutant": "line 44: <> -> = (rel) on booking_state 'X'", + "status": "killed", + "hidden": "0/7", + "failed_tests": [ + "busy_courts_only", + "quiet_courts_dropped", + "one_day_only", + "cancelled_ignored", + "totals_per_court", + "court_filter", + "alphabetical_order" + ] + }, + { + "object": "{{P}}COURT_OLD", + "mutant": "line 62: bookings < 2 -> bookings < 1 (rel)", + "status": "killed", + "hidden": "5/7", + "failed_tests": [ + "busy_courts_only", + "quiet_courts_dropped" + ] + } + ], + "valid": 2, + "killed": 2, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1094/reference/court_old.prog.abap b/tasks_gen/train/G1094/reference/court_old.prog.abap new file mode 100644 index 0000000..f0b4f4a --- /dev/null +++ b/tasks_gen/train/G1094/reference/court_old.prog.abap @@ -0,0 +1,118 @@ +REPORT {{p}}court_old. + +TYPES: BEGIN OF ty_line, + court TYPE {{p}}court-court_id, + bookings TYPE i, + total_min TYPE i, + charge TYPE {{p}}court-hourly_rate, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. +TYPES tt_court TYPE RANGE OF {{p}}court-court_id. + +DATA gv_court TYPE {{p}}court-court_id. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_court FOR gv_court. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + METHODS read_usage + IMPORTING iv_date TYPE d + it_court TYPE tt_court + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS show + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_usage. + SELECT c~court_id AS court, + COUNT( * ) AS bookings, + SUM( b~duration_min ) AS total_min, + SUM( b~duration_min * c~hourly_rate / 60 ) AS charge + FROM {{p}}booking AS b + INNER JOIN {{p}}court AS c ON c~court_id = b~court_id + WHERE b~booking_date = @iv_date + AND b~booking_state <> 'X' + AND b~court_id IN @it_court + GROUP BY c~court_id + ORDER BY c~court_id + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + DELETE rt_lines WHERE bookings < 2. + ENDMETHOD. + + METHOD show. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->read_usage( iv_date = p_date it_court = s_court[] ). + go_report->show( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_court FOR TESTING. + METHODS quiet_courts_dropped FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}COURT' ) ( '{{P}}BOOKING' ) ) ). + + DATA lt_court TYPE STANDARD TABLE OF {{p}}court WITH EMPTY KEY. + lt_court = VALUE #( + ( court_id = 'A01' court_name = 'A Court' court_kind = 'I' hourly_rate = '12.00' ) + ( court_id = 'B01' court_name = 'B Court' court_kind = 'O' hourly_rate = '20.00' ) ). + go_env->insert_test_data( lt_court ). + + DATA lt_booking TYPE STANDARD TABLE OF {{p}}booking WITH EMPTY KEY. + lt_booking = VALUE #( + ( booking_id = 1 court_id = 'A01' booking_date = '20260401' duration_min = 60 booking_state = 'B' ) + ( booking_id = 2 court_id = 'A01' booking_date = '20260401' duration_min = 30 booking_state = 'B' ) + ( booking_id = 3 court_id = 'B01' booking_date = '20260401' duration_min = 60 booking_state = 'B' ) + ( booking_id = 4 court_id = 'B01' booking_date = '20260401' duration_min = 60 booking_state = 'X' ) ). + go_env->insert_test_data( lt_booking ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_court. + DATA lv_court TYPE {{p}}court-court_id. + DATA lv_expected TYPE {{p}}court-hourly_rate. + lv_court = 'A01'. + lv_expected = '18.00'. + + DATA(lt_lines) = NEW lcl_report( )->read_usage( iv_date = '20260401' it_court = VALUE #( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = lv_court act = lt_lines[ 1 ]-court ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-bookings ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = lt_lines[ 1 ]-total_min ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-charge ). + ENDMETHOD. + + METHOD quiet_courts_dropped. + DATA lv_court TYPE {{p}}court-court_id. + lv_court = 'A01'. + + DATA(lt_lines) = NEW lcl_report( )->read_usage( iv_date = '20260401' it_court = VALUE #( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = lv_court act = lt_lines[ 1 ]-court ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1094/seed/booking.tabl.asabap b/tasks_gen/train/G1094/seed/booking.tabl.asabap new file mode 100644 index 0000000..09b40a8 --- /dev/null +++ b/tasks_gen/train/G1094/seed/booking.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Court bookings' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}booking { + key client : abap.clnt not null; + key booking_id : abap.int4 not null; + court_id : abap.char(4); + member_id : abap.char(10); + booking_date : abap.dats; + duration_min : abap.int4; + booking_state : abap.char(1); +} diff --git a/tasks_gen/train/G1094/seed/court.tabl.asabap b/tasks_gen/train/G1094/seed/court.tabl.asabap new file mode 100644 index 0000000..65cf196 --- /dev/null +++ b/tasks_gen/train/G1094/seed/court.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Tennis courts' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}court { + key client : abap.clnt not null; + key court_id : abap.char(4) not null; + court_name : abap.char(20); + court_kind : abap.char(1); + hourly_rate : abap.dec(9,2); +} diff --git a/tasks_gen/train/G1094/seed/court_old.prog.abap b/tasks_gen/train/G1094/seed/court_old.prog.abap new file mode 100644 index 0000000..a943645 --- /dev/null +++ b/tasks_gen/train/G1094/seed/court_old.prog.abap @@ -0,0 +1,92 @@ +REPORT {{p}}court_old. + +TYPES: BEGIN OF ty_result, + court TYPE {{p}}court-court_id, + bookings TYPE i, + total_min TYPE i, + charge TYPE p LENGTH 11 DECIMALS 2, + END OF ty_result. + +DATA: gt_booking TYPE STANDARD TABLE OF {{p}}booking, + gt_court TYPE STANDARD TABLE OF {{p}}court, + gt_result TYPE STANDARD TABLE OF ty_result, + gs_booking TYPE {{p}}booking, + gs_court TYPE {{p}}court, + gs_result TYPE ty_result, + gs_next TYPE ty_result, + gv_court TYPE {{p}}court-court_id, + gv_fee TYPE p LENGTH 11 DECIMALS 2, + gv_lines TYPE i, + gv_outer TYPE i, + gv_inner TYPE i, + gv_i TYPE i, + gv_j TYPE i, + gv_k TYPE i, + go_alv TYPE REF TO cl_salv_table. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_court FOR gv_court. + +START-OF-SELECTION. + + SELECT * FROM {{p}}court INTO TABLE gt_court. + + SELECT * FROM {{p}}booking INTO TABLE gt_booking WHERE booking_date = p_date. + + LOOP AT gt_booking INTO gs_booking. + IF gs_booking-booking_state <> 'X'. + IF gs_booking-court_id IN s_court. + READ TABLE gt_court INTO gs_court WITH KEY court_id = gs_booking-court_id. + IF sy-subrc = 0. + CLEAR gv_fee. + gv_fee = gs_booking-duration_min * gs_court-hourly_rate / 60. + READ TABLE gt_result INTO gs_result WITH KEY court = gs_booking-court_id. + IF sy-subrc = 0. + gv_i = sy-tabix. + gs_result-bookings = gs_result-bookings + 1. + gs_result-total_min = gs_result-total_min + gs_booking-duration_min. + gs_result-charge = gs_result-charge + gv_fee. + MODIFY gt_result FROM gs_result INDEX gv_i. + ELSE. + CLEAR gs_result. + gs_result-court = gs_booking-court_id. + gs_result-bookings = 1. + gs_result-total_min = gs_booking-duration_min. + gs_result-charge = gv_fee. + APPEND gs_result TO gt_result. + ENDIF. + ENDIF. + ENDIF. + ENDIF. + ENDLOOP. + + LOOP AT gt_result INTO gs_result. + IF gs_result-bookings < 2. + DELETE gt_result INDEX sy-tabix. + ENDIF. + ENDLOOP. + + gv_lines = lines( gt_result ). + gv_outer = gv_lines - 1. + DO gv_outer TIMES. + gv_i = sy-index. + gv_inner = gv_lines - gv_i. + DO gv_inner TIMES. + gv_j = sy-index. + gv_k = gv_j + 1. + READ TABLE gt_result INTO gs_result INDEX gv_j. + READ TABLE gt_result INTO gs_next INDEX gv_k. + IF gs_next-court < gs_result-court. + MODIFY gt_result FROM gs_next INDEX gv_j. + MODIFY gt_result FROM gs_result INDEX gv_k. + ENDIF. + ENDDO. + ENDDO. + + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = go_alv + CHANGING t_table = gt_result ). + go_alv->display( ). + CATCH cx_salv_msg. + ENDTRY. diff --git a/tasks_gen/train/G1094/seed/court_seed.clas.abap b/tasks_gen/train/G1094/seed/court_seed.clas.abap new file mode 100644 index 0000000..173d467 --- /dev/null +++ b/tasks_gen/train/G1094/seed/court_seed.clas.abap @@ -0,0 +1,32 @@ +CLASS {{p}}court_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}court_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}booking. + DELETE FROM {{p}}court. + + INSERT {{p}}court FROM TABLE @( VALUE #( + ( court_id = 'C001' court_name = 'Centre Court' court_kind = 'I' hourly_rate = '12.00' ) + ( court_id = 'C002' court_name = 'North Court' court_kind = 'O' hourly_rate = '16.00' ) + ( court_id = 'C003' court_name = 'East Court' court_kind = 'I' hourly_rate = '20.00' ) + ( court_id = 'C004' court_name = 'West Court' court_kind = 'O' hourly_rate = '10.00' ) ) ). + + INSERT {{p}}booking FROM TABLE @( VALUE #( + ( booking_id = 1 court_id = 'C001' member_id = 'M01' booking_date = '20260401' duration_min = 60 booking_state = 'B' ) + ( booking_id = 2 court_id = 'C001' member_id = 'M02' booking_date = '20260401' duration_min = 30 booking_state = 'B' ) + ( booking_id = 3 court_id = 'C001' member_id = 'M03' booking_date = '20260401' duration_min = 60 booking_state = 'X' ) + ( booking_id = 4 court_id = 'C002' member_id = 'M04' booking_date = '20260401' duration_min = 90 booking_state = 'B' ) + ( booking_id = 5 court_id = 'C002' member_id = 'M05' booking_date = '20260401' duration_min = 30 booking_state = 'B' ) + ( booking_id = 6 court_id = 'C002' member_id = 'M06' booking_date = '20260401' duration_min = 60 booking_state = 'X' ) + ( booking_id = 7 court_id = 'C003' member_id = 'M07' booking_date = '20260401' duration_min = 60 booking_state = 'B' ) + ( booking_id = 8 court_id = 'C004' member_id = 'M08' booking_date = '20260401' duration_min = 45 booking_state = 'B' ) + ( booking_id = 9 court_id = 'C002' member_id = 'M09' booking_date = '20260331' duration_min = 60 booking_state = 'B' ) + ( booking_id = 10 court_id = 'C003' member_id = 'M10' booking_date = '20260331' duration_min = 60 booking_state = 'B' ) + ( booking_id = 11 court_id = 'C003' member_id = 'M11' booking_date = '20260331' duration_min = 60 booking_state = 'B' ) ) ). + + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1094/spec.md b/tasks_gen/train/G1094/spec.md new file mode 100644 index 0000000..a6f713f --- /dev/null +++ b/tasks_gen/train/G1094/spec.md @@ -0,0 +1,44 @@ +# 1. Goal +A tennis club rents out its courts by the hour. The club manager wants a daily +overview of how busy each court was. The existing report {{P}}COURT_OLD in +package $TMP already produces this overview, but its source has grown messy over +the years. Rework the source so that the team can maintain it again. The output +must not change. + +# 2. Open questions +None. + +# 3. Context +- {{P}}COURT holds the court master data: COURT_ID (CHAR 4, key), + COURT_NAME (CHAR 20), COURT_KIND (CHAR 1), HOURLY_RATE (DEC 9,2). +- {{P}}BOOKING holds the bookings: BOOKING_ID (INT4, key), COURT_ID (CHAR 4), + MEMBER_ID (CHAR 10), BOOKING_DATE (DATS), DURATION_MIN (INT4), + BOOKING_STATE (CHAR 1). +- Both tables are in package $TMP and contain data. +- {{P}}COURT_OLD is active. It is the object that you change. + +# 4. Contract +- {{P}}COURT_OLD remains an executable program in package $TMP with the same + name. +- The selection screen remains as it is today: P_DATE (DATS, obligatory) and + S_COURT (select-option on the court). +- The ALV list remains as it is today. Its columns are COURT, BOOKINGS, + TOTAL_MIN and CHARGE. CHARGE is a packed number with two decimals. + +# 5. Business rules +The current source of {{P}}COURT_OLD defines what the report does. Read that +source and keep every one of its rules. Do not add rules and do not remove +rules. The figures, the rows and the order of the rows in the list must stay +exactly the same for every input. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Short methods. Same behavior. Same selection + screen. +- Out of scope: {{P}}COURT and {{P}}BOOKING. + +# 7. Acceptance +- {{P}}COURT_OLD is active and free of syntax errors. +- The list that the report produces is unchanged. +- The hidden tests pass. +- Add ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1094/task.json b/tasks_gen/train/G1094/task.json new file mode 100644 index 0000000..33dae44 --- /dev/null +++ b/tasks_gen/train/G1094/task.json @@ -0,0 +1,73 @@ +{ + "id": "G1094", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}COURT", + "file": "seed/court.tabl.asabap", + "description": "Tennis court master data" + }, + { + "type": "TABL", + "name": "{{P}}BOOKING", + "file": "seed/booking.tabl.asabap", + "description": "Court bookings" + }, + { + "type": "PROG", + "name": "{{P}}COURT_OLD", + "file": "seed/court_old.prog.abap", + "description": "Legacy court usage report" + }, + { + "type": "CLAS", + "name": "{{P}}COURT_SEED", + "file": "seed/court_seed.clas.abap", + "description": "Court and booking seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}COURT_OLD", + "parameters": [ + "p_date", + "s_court" + ] + } + ], + "out_of_scope": [ + "{{P}}COURT", + "{{P}}BOOKING" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}COURT_HIDDEN", + "file": "hidden/court_hidden.clas.abap", + "description": "Court usage hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}COURT_OLD", + "file": "reference/court_old.prog.abap", + "description": "Refactored court usage report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1095/faulty/m0_heli_pricer.clas.abap b/tasks_gen/train/G1095/faulty/m0_heli_pricer.clas.abap new file mode 100644 index 0000000..af6db7c --- /dev/null +++ b/tasks_gen/train/G1095/faulty/m0_heli_pricer.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}heli_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_heli_invalid. + + PRIVATE SECTION. + CONSTANTS: + c_rate_scenic TYPE decfloat34 VALUE '8', + c_rate_training TYPE decfloat34 VALUE '12', + c_pax_surcharge TYPE decfloat34 VALUE '15', + c_member_rate TYPE decfloat34 VALUE '0.875', + c_min_fee TYPE decfloat34 VALUE '50', + c_max_fee TYPE decfloat34 VALUE '1500', + c_max_pax TYPE i VALUE 6, + c_min_minutes TYPE i VALUE 5. + + METHODS check_input + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + RAISING + {{p}}cx_heli_invalid. + + METHODS scenic_fee + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS round_fee + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS apply_limits + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}heli_pricer IMPLEMENTATION. + + METHOD calculate. + check_input( iv_passengers = iv_passengers iv_minutes = iv_minutes ). + + ev_skip = abap_false. + + CASE iv_tour_type. + WHEN 'SC'. + ev_fee = apply_limits( round_fee( scenic_fee( iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member ) ) ). + WHEN 'TR'. + ev_fee = apply_limits( round_fee( CONV decfloat34( iv_minutes ) / c_rate_training ) ). + WHEN 'CH'. + ev_skip = abap_true. + WHEN OTHERS. + ev_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD check_input. + IF iv_passengers < 1 OR iv_passengers > c_max_pax. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-passengers. + ENDIF. + IF iv_minutes < c_min_minutes. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-minutes. + ENDIF. + ENDMETHOD. + + METHOD scenic_fee. + rv_fee = CONV decfloat34( iv_minutes ) * c_rate_scenic + + CONV decfloat34( iv_passengers ) * c_pax_surcharge. + IF iv_member = abap_true. + rv_fee = rv_fee * c_member_rate. + ENDIF. + ENDMETHOD. + + METHOD round_fee. + rv_fee = round( val = iv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. + + METHOD apply_limits. + rv_fee = iv_fee. + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ELSEIF rv_fee > c_max_fee. + rv_fee = c_max_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/faulty/m1_heli_fee.func.abap b/tasks_gen/train/G1095/faulty/m1_heli_fee.func.abap new file mode 100644 index 0000000..03ed7e2 --- /dev/null +++ b/tasks_gen/train/G1095/faulty/m1_heli_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}heli_fee + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_passengers + invalid_minutes. + + DATA lo_pricer TYPE REF TO {{p}}heli_pricer. + DATA lv_fee TYPE decfloat34. + + lo_pricer = NEW #( ). + + TRY. + lo_pricer->calculate( + EXPORTING + iv_tour_type = iv_tour_type + iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member + IMPORTING + ev_skip = ev_skip + ev_fee = lv_fee ). + CATCH {{p}}cx_heli_invalid INTO DATA(lo_error). + CASE lo_error->mv_reason. + WHEN {{p}}cx_heli_invalid=>co_reason-passengers. + RAISE invalid_passengers. + WHEN OTHERS. + RAISE invalid_minutes. + ENDCASE. + ENDTRY. + + IF ev_skip <> abap_false. + cv_fee = lv_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1095/faulty/m2_heli_pricer.clas.abap b/tasks_gen/train/G1095/faulty/m2_heli_pricer.clas.abap new file mode 100644 index 0000000..67a6b28 --- /dev/null +++ b/tasks_gen/train/G1095/faulty/m2_heli_pricer.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}heli_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_heli_invalid. + + PRIVATE SECTION. + CONSTANTS: + c_rate_scenic TYPE decfloat34 VALUE '8', + c_rate_training TYPE decfloat34 VALUE '12', + c_pax_surcharge TYPE decfloat34 VALUE '15', + c_member_rate TYPE decfloat34 VALUE '0.875', + c_min_fee TYPE decfloat34 VALUE '50', + c_max_fee TYPE decfloat34 VALUE '1500', + c_max_pax TYPE i VALUE 6, + c_min_minutes TYPE i VALUE 5. + + METHODS check_input + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + RAISING + {{p}}cx_heli_invalid. + + METHODS scenic_fee + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS round_fee + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS apply_limits + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}heli_pricer IMPLEMENTATION. + + METHOD calculate. + check_input( iv_passengers = iv_passengers iv_minutes = iv_minutes ). + + ev_skip = abap_false. + + CASE iv_tour_type. + WHEN 'SC'. + ev_fee = apply_limits( round_fee( scenic_fee( iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member ) ) ). + WHEN 'TR'. + ev_fee = apply_limits( round_fee( CONV decfloat34( iv_minutes ) * c_rate_training ) ). + WHEN 'CH'. + ev_skip = abap_true. + WHEN OTHERS. + ev_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD check_input. + IF iv_passengers < 1 AND iv_passengers > c_max_pax. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-passengers. + ENDIF. + IF iv_minutes < c_min_minutes. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-minutes. + ENDIF. + ENDMETHOD. + + METHOD scenic_fee. + rv_fee = CONV decfloat34( iv_minutes ) * c_rate_scenic + + CONV decfloat34( iv_passengers ) * c_pax_surcharge. + IF iv_member = abap_true. + rv_fee = rv_fee * c_member_rate. + ENDIF. + ENDMETHOD. + + METHOD round_fee. + rv_fee = round( val = iv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. + + METHOD apply_limits. + rv_fee = iv_fee. + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ELSEIF rv_fee > c_max_fee. + rv_fee = c_max_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/faulty/m3_heli_fee.func.abap b/tasks_gen/train/G1095/faulty/m3_heli_fee.func.abap new file mode 100644 index 0000000..f104b8e --- /dev/null +++ b/tasks_gen/train/G1095/faulty/m3_heli_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}heli_fee + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_passengers + invalid_minutes. + + DATA lo_pricer TYPE REF TO {{p}}heli_pricer. + DATA lv_fee TYPE decfloat34. + + lo_pricer = NEW #( ). + + TRY. + lo_pricer->calculate( + EXPORTING + iv_tour_type = iv_tour_type + iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member + IMPORTING + ev_skip = ev_skip + ev_fee = lv_fee ). + CATCH {{p}}cx_heli_invalid INTO DATA(lo_error). + CASE lo_error->mv_reason. + WHEN {{p}}cx_heli_invalid=>co_reason-passengers. + RAISE invalid_passengers. + WHEN OTHERS. + RAISE invalid_minutes. + ENDCASE. + ENDTRY. + + IF ev_skip = abap_true. + cv_fee = lv_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1095/faulty/m4_heli_pricer.clas.abap b/tasks_gen/train/G1095/faulty/m4_heli_pricer.clas.abap new file mode 100644 index 0000000..f686bd4 --- /dev/null +++ b/tasks_gen/train/G1095/faulty/m4_heli_pricer.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}heli_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_heli_invalid. + + PRIVATE SECTION. + CONSTANTS: + c_rate_scenic TYPE decfloat34 VALUE '8', + c_rate_training TYPE decfloat34 VALUE '12', + c_pax_surcharge TYPE decfloat34 VALUE '15', + c_member_rate TYPE decfloat34 VALUE '0.875', + c_min_fee TYPE decfloat34 VALUE '50', + c_max_fee TYPE decfloat34 VALUE '1500', + c_max_pax TYPE i VALUE 6, + c_min_minutes TYPE i VALUE 5. + + METHODS check_input + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + RAISING + {{p}}cx_heli_invalid. + + METHODS scenic_fee + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS round_fee + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS apply_limits + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}heli_pricer IMPLEMENTATION. + + METHOD calculate. + check_input( iv_passengers = iv_passengers iv_minutes = iv_minutes ). + + ev_skip = abap_false. + + CASE iv_tour_type. + WHEN 'SC'. + ev_fee = apply_limits( round_fee( scenic_fee( iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member ) ) ). + WHEN 'TR'. + ev_fee = apply_limits( round_fee( CONV decfloat34( iv_minutes ) * c_rate_training ) ). + WHEN 'CH'. + ev_skip = abap_true. + WHEN OTHERS. + ev_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD check_input. + IF iv_passengers < 1 OR iv_passengers > c_max_pax. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-passengers. + ENDIF. + IF iv_minutes < c_min_minutes. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-minutes. + ENDIF. + ENDMETHOD. + + METHOD scenic_fee. + rv_fee = CONV decfloat34( iv_minutes ) * c_rate_scenic + + CONV decfloat34( iv_passengers ) * c_pax_surcharge. + IF iv_member <> abap_true. + rv_fee = rv_fee * c_member_rate. + ENDIF. + ENDMETHOD. + + METHOD round_fee. + rv_fee = round( val = iv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. + + METHOD apply_limits. + rv_fee = iv_fee. + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ELSEIF rv_fee > c_max_fee. + rv_fee = c_max_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/generation.json b/tasks_gen/train/G1095/generation.json new file mode 100644 index 0000000..97dc3b2 --- /dev/null +++ b/tasks_gen/train/G1095/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1095", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1095/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1095/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..f127b09 --- /dev/null +++ b/tasks_gen/train/G1095/hidden/t14_hidden.clas.abap @@ -0,0 +1,128 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS call + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool DEFAULT abap_false + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS scenic_fee FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS scenic_minimum FOR TESTING. + METHODS scenic_maximum FOR TESTING. + METHODS training_fee FOR TESTING. + METHODS training_ignores_member FOR TESTING. + METHODS charter_skips FOR TESTING. + METHODS other_type_zero FOR TESTING. + METHODS rejects_zero_passengers FOR TESTING. + METHODS rejects_too_many_passengers FOR TESTING. + METHODS rejects_short_flight FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}HELI_FEE' + EXPORTING + iv_tour_type = iv_tour_type + iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_passengers = 1 + invalid_minutes = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD scenic_fee. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 2 iv_minutes = 10 + IMPORTING ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 110 ) act = lv_fee ). + cl_abap_unit_assert=>assert_false( lv_skip ). + ENDMETHOD. + + METHOD member_discount. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 3 iv_minutes = 7 iv_member = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '88.38' ) act = lv_fee ). + ENDMETHOD. + + METHOD scenic_minimum. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 1 iv_minutes = 5 iv_member = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 50 ) act = lv_fee ). + ENDMETHOD. + + METHOD scenic_maximum. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 6 iv_minutes = 200 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1500 ) act = lv_fee ). + ENDMETHOD. + + METHOD training_fee. + call( EXPORTING iv_tour_type = `TR` iv_passengers = 2 iv_minutes = 10 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD training_ignores_member. + call( EXPORTING iv_tour_type = `TR` iv_passengers = 2 iv_minutes = 10 iv_member = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD charter_skips. + call( EXPORTING iv_tour_type = `CH` iv_passengers = 2 iv_minutes = 10 iv_fee = 7 + IMPORTING ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_fee ). + ENDMETHOD. + + METHOD other_type_zero. + call( EXPORTING iv_tour_type = `XX` iv_passengers = 2 iv_minutes = 10 + IMPORTING ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_fee ). + cl_abap_unit_assert=>assert_false( lv_skip ). + ENDMETHOD. + + METHOD rejects_zero_passengers. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 0 iv_minutes = 10 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD rejects_too_many_passengers. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 7 iv_minutes = 10 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD rejects_short_flight. + call( EXPORTING iv_tour_type = `SC` iv_passengers = 2 iv_minutes = 4 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/mutation.json b/tasks_gen/train/G1095/mutation.json new file mode 100644 index 0000000..94b97be --- /dev/null +++ b/tasks_gen/train/G1095/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1095", + "mutants": [ + { + "object": "{{P}}HELI_PRICER", + "mutant": "line 72: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "TRAINING_FEE", + "TRAINING_IGNORES_MEMBER" + ] + }, + { + "object": "{{P}}HELI_FEE", + "mutant": "line 39: = -> <> (eq)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "CHARTER_SKIPS", + "MEMBER_DISCOUNT", + "OTHER_TYPE_ZERO", + "SCENIC_FEE", + "SCENIC_MAXIMUM", + "SCENIC_MINIMUM", + "TRAINING_FEE", + "TRAINING_IGNORES_MEMBER" + ] + }, + { + "object": "{{P}}HELI_PRICER", + "mutant": "line 81: OR -> AND (logic)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "REJECTS_TOO_MANY_PASSENGERS", + "REJECTS_ZERO_PASSENGERS" + ] + }, + { + "object": "{{P}}HELI_FEE", + "mutant": "line 39: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "CHARTER_SKIPS", + "MEMBER_DISCOUNT", + "OTHER_TYPE_ZERO", + "SCENIC_FEE", + "SCENIC_MAXIMUM", + "SCENIC_MINIMUM", + "TRAINING_FEE", + "TRAINING_IGNORES_MEMBER" + ] + }, + { + "object": "{{P}}HELI_PRICER", + "mutant": "line 96: = -> <> (eq)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "MEMBER_DISCOUNT", + "SCENIC_FEE", + "SCENIC_MAXIMUM", + "SCENIC_MINIMUM" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1095/reference/cx_heli_invalid.clas.abap b/tasks_gen/train/G1095/reference/cx_heli_invalid.clas.abap new file mode 100644 index 0000000..42761b6 --- /dev/null +++ b/tasks_gen/train/G1095/reference/cx_heli_invalid.clas.abap @@ -0,0 +1,27 @@ +CLASS {{p}}cx_heli_invalid DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + CONSTANTS: + BEGIN OF co_reason, + passengers TYPE string VALUE 'PASSENGERS', + minutes TYPE string VALUE 'MINUTES', + END OF co_reason. + + DATA mv_reason TYPE string READ-ONLY. + + METHODS constructor + IMPORTING + iv_reason TYPE string. +ENDCLASS. + + +CLASS {{p}}cx_heli_invalid IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/reference/heli_fee.func.abap b/tasks_gen/train/G1095/reference/heli_fee.func.abap new file mode 100644 index 0000000..b8dfab7 --- /dev/null +++ b/tasks_gen/train/G1095/reference/heli_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}heli_fee + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_passengers + invalid_minutes. + + DATA lo_pricer TYPE REF TO {{p}}heli_pricer. + DATA lv_fee TYPE decfloat34. + + lo_pricer = NEW #( ). + + TRY. + lo_pricer->calculate( + EXPORTING + iv_tour_type = iv_tour_type + iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member + IMPORTING + ev_skip = ev_skip + ev_fee = lv_fee ). + CATCH {{p}}cx_heli_invalid INTO DATA(lo_error). + CASE lo_error->mv_reason. + WHEN {{p}}cx_heli_invalid=>co_reason-passengers. + RAISE invalid_passengers. + WHEN OTHERS. + RAISE invalid_minutes. + ENDCASE. + ENDTRY. + + IF ev_skip = abap_false. + cv_fee = lv_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1095/reference/heli_pricer.clas.abap b/tasks_gen/train/G1095/reference/heli_pricer.clas.abap new file mode 100644 index 0000000..7397c87 --- /dev/null +++ b/tasks_gen/train/G1095/reference/heli_pricer.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}heli_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS calculate + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_heli_invalid. + + PRIVATE SECTION. + CONSTANTS: + c_rate_scenic TYPE decfloat34 VALUE '8', + c_rate_training TYPE decfloat34 VALUE '12', + c_pax_surcharge TYPE decfloat34 VALUE '15', + c_member_rate TYPE decfloat34 VALUE '0.875', + c_min_fee TYPE decfloat34 VALUE '50', + c_max_fee TYPE decfloat34 VALUE '1500', + c_max_pax TYPE i VALUE 6, + c_min_minutes TYPE i VALUE 5. + + METHODS check_input + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + RAISING + {{p}}cx_heli_invalid. + + METHODS scenic_fee + IMPORTING + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS round_fee + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS apply_limits + IMPORTING + iv_fee TYPE decfloat34 + RETURNING + VALUE(rv_fee) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}heli_pricer IMPLEMENTATION. + + METHOD calculate. + check_input( iv_passengers = iv_passengers iv_minutes = iv_minutes ). + + ev_skip = abap_false. + + CASE iv_tour_type. + WHEN 'SC'. + ev_fee = apply_limits( round_fee( scenic_fee( iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member ) ) ). + WHEN 'TR'. + ev_fee = apply_limits( round_fee( CONV decfloat34( iv_minutes ) * c_rate_training ) ). + WHEN 'CH'. + ev_skip = abap_true. + WHEN OTHERS. + ev_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD check_input. + IF iv_passengers < 1 OR iv_passengers > c_max_pax. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-passengers. + ENDIF. + IF iv_minutes < c_min_minutes. + RAISE EXCEPTION TYPE {{p}}cx_heli_invalid + EXPORTING + iv_reason = {{p}}cx_heli_invalid=>co_reason-minutes. + ENDIF. + ENDMETHOD. + + METHOD scenic_fee. + rv_fee = CONV decfloat34( iv_minutes ) * c_rate_scenic + + CONV decfloat34( iv_passengers ) * c_pax_surcharge. + IF iv_member = abap_true. + rv_fee = rv_fee * c_member_rate. + ENDIF. + ENDMETHOD. + + METHOD round_fee. + rv_fee = round( val = iv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ENDMETHOD. + + METHOD apply_limits. + rv_fee = iv_fee. + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ELSEIF rv_fee > c_max_fee. + rv_fee = c_max_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/reference/heli_pricer_testclasses.clas.abap b/tasks_gen/train/G1095/reference/heli_pricer_testclasses.clas.abap new file mode 100644 index 0000000..df8edcb --- /dev/null +++ b/tasks_gen/train/G1095/reference/heli_pricer_testclasses.clas.abap @@ -0,0 +1,97 @@ +CLASS ltc_pricer DEFINITION FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}heli_pricer. + + METHODS setup. + METHODS scenic_fee FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS training_fee FOR TESTING. + METHODS charter_skips FOR TESTING. + METHODS rejects_zero_passengers FOR TESTING. +ENDCLASS. + + +CLASS ltc_pricer IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW #( ). + ENDMETHOD. + + METHOD scenic_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + mo_cut->calculate( + EXPORTING + iv_tour_type = `SC` + iv_passengers = 2 + iv_minutes = 10 + iv_member = abap_false + IMPORTING + ev_skip = lv_skip + ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 110 ) act = lv_fee ). + cl_abap_unit_assert=>assert_false( lv_skip ). + ENDMETHOD. + + METHOD member_discount. + DATA lv_fee TYPE decfloat34. + mo_cut->calculate( + EXPORTING + iv_tour_type = `SC` + iv_passengers = 3 + iv_minutes = 7 + iv_member = abap_true + IMPORTING + ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '88.38' ) act = lv_fee ). + ENDMETHOD. + + METHOD training_fee. + DATA lv_fee TYPE decfloat34. + mo_cut->calculate( + EXPORTING + iv_tour_type = `TR` + iv_passengers = 2 + iv_minutes = 10 + iv_member = abap_false + IMPORTING + ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD charter_skips. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + mo_cut->calculate( + EXPORTING + iv_tour_type = `CH` + iv_passengers = 2 + iv_minutes = 10 + iv_member = abap_false + IMPORTING + ev_skip = lv_skip + ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_true( lv_skip ). + ENDMETHOD. + + METHOD rejects_zero_passengers. + TRY. + mo_cut->calculate( + EXPORTING + iv_tour_type = `SC` + iv_passengers = 0 + iv_minutes = 10 + iv_member = abap_false + IMPORTING + ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>fail( ). + CATCH {{p}}cx_heli_invalid INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_heli_invalid=>co_reason-passengers + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/reference/t14_test.clas.abap b/tasks_gen/train/G1095/reference/t14_test.clas.abap new file mode 100644 index 0000000..351156e --- /dev/null +++ b/tasks_gen/train/G1095/reference/t14_test.clas.abap @@ -0,0 +1,85 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS call_fee + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool DEFAULT abap_false + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING + ev_fee TYPE decfloat34 + ev_skip TYPE abap_bool + ev_subrc TYPE sysubrc. + + METHODS scenic_price FOR TESTING. + METHODS member_price FOR TESTING. + METHODS training_price FOR TESTING. + METHODS charter_price FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + + METHOD call_fee. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}HELI_FEE' + EXPORTING + iv_tour_type = iv_tour_type + iv_passengers = iv_passengers + iv_minutes = iv_minutes + iv_member = iv_member + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_passengers = 1 + invalid_minutes = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD scenic_price. + call_fee( EXPORTING iv_tour_type = `SC` iv_passengers = 2 iv_minutes = 10 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 110 ) act = lv_fee ). + cl_abap_unit_assert=>assert_false( lv_skip ). + ENDMETHOD. + + METHOD member_price. + call_fee( EXPORTING iv_tour_type = `SC` iv_passengers = 3 iv_minutes = 7 iv_member = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '88.38' ) act = lv_fee ). + ENDMETHOD. + + METHOD training_price. + call_fee( EXPORTING iv_tour_type = `TR` iv_passengers = 2 iv_minutes = 10 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD charter_price. + call_fee( EXPORTING iv_tour_type = `CH` iv_passengers = 2 iv_minutes = 10 iv_fee = 7 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_input. + call_fee( EXPORTING iv_tour_type = `SC` iv_passengers = 0 iv_minutes = 10 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + + call_fee( EXPORTING iv_tour_type = `TR` iv_passengers = 2 iv_minutes = 4 + IMPORTING ev_subrc = DATA(lv_rc_minutes) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc_minutes ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1095/seed/heli_fee.func.abap b/tasks_gen/train/G1095/seed/heli_fee.func.abap new file mode 100644 index 0000000..39a2d1e --- /dev/null +++ b/tasks_gen/train/G1095/seed/heli_fee.func.abap @@ -0,0 +1,17 @@ +FUNCTION {{p}}heli_fee + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_passengers + invalid_minutes. + + " Skeleton: the implementation of this flight module is still missing. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1095/seed/heli_sample.func.abap b/tasks_gen/train/G1095/seed/heli_sample.func.abap new file mode 100644 index 0000000..da02786 --- /dev/null +++ b/tasks_gen/train/G1095/seed/heli_sample.func.abap @@ -0,0 +1,17 @@ +FUNCTION {{p}}heli_sample + IMPORTING + iv_tour_type TYPE string + iv_passengers TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_passengers + invalid_minutes. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1095/spec.md b/tasks_gen/train/G1095/spec.md new file mode 100644 index 0000000..c5ca7f6 --- /dev/null +++ b/tasks_gen/train/G1095/spec.md @@ -0,0 +1,53 @@ +# 1. Goal +Calculate the price of a sightseeing flight for a helicopter operator. The +booking program calls one function module for each flight that a customer books. +The function module must use the interface of the sample module, because the +booking program calls all flight modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_HELI exists in package $TMP. +- Function module {{P}}HELI_FEE exists in this group. It has the correct + interface, but its implementation is empty. +- Function module {{P}}HELI_SAMPLE exists in this group. It is the sample + module. Its interface is the interface that all flight modules must use. +- A flight has a tour type, a number of passengers, and a flight time in + minutes. +- A customer is a member of the flying club or not. + +# 4. Contract +- Implement the function module {{P}}HELI_FEE in function group {{P}}FG_HELI. +- Keep exactly the interface of {{P}}HELI_SAMPLE: the same parameter names, + kinds, types, and exceptions. + +# 5. Business rules +1. If the number of passengers is less than 1 or greater than 6, raise the + exception INVALID_PASSENGERS. +2. If the flight time is less than 5 minutes, raise the exception + INVALID_MINUTES. +3. Set the skip flag to false, except in rule 6. +4. Tour type SC (sightseeing circuit): + - The gross price is 8.00 per minute plus 15.00 per passenger. + - If the customer is a member, subtract 12.5 % of the gross price. + - Round the result half up to 2 decimals. + - The minimum price is 50.00. The maximum price is 1500.00. +5. Tour type TR (training flight): + - The gross price is 12.00 per minute. There is no passenger surcharge. + - The member discount does not apply. + - Round the result half up to 2 decimals. + - The minimum price is 50.00. The maximum price is 1500.00. +6. Tour type CH (charter flight): set the skip flag to true. Do not change the + price. +7. All other tour types: the price is 0. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Use Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}HELI_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1095/task.json b/tasks_gen/train/G1095/task.json new file mode 100644 index 0000000..3577f29 --- /dev/null +++ b/tasks_gen/train/G1095/task.json @@ -0,0 +1,108 @@ +{ + "id": "G1095", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_HELI", + "description": "Flight booking modules" + }, + { + "type": "FUNC", + "name": "{{P}}HELI_FEE", + "functionGroup": "{{P}}FG_HELI", + "file": "seed/heli_fee.func.abap", + "description": "Skeleton of the flight price module (empty implementation)" + }, + { + "type": "FUNC", + "name": "{{P}}HELI_SAMPLE", + "functionGroup": "{{P}}FG_HELI", + "file": "seed/heli_sample.func.abap", + "description": "Sample flight module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}HELI_FEE", + "functionGroup": "{{P}}FG_HELI", + "params": [ + { + "name": "iv_tour_type", + "type": "string" + }, + { + "name": "iv_passengers", + "type": "i" + }, + { + "name": "iv_minutes", + "type": "i" + }, + { + "name": "iv_member", + "type": "abap_bool" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}HELI_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_HELI_INVALID", + "file": "reference/cx_heli_invalid.clas.abap", + "description": "Invalid flight input" + }, + { + "type": "CLAS", + "name": "{{P}}HELI_PRICER", + "file": "reference/heli_pricer.clas.abap", + "testclasses_file": "reference/heli_pricer_testclasses.clas.abap", + "description": "Pure flight price logic" + }, + { + "type": "FUNC", + "name": "{{P}}HELI_FEE", + "functionGroup": "{{P}}FG_HELI", + "file": "reference/heli_fee.func.abap", + "description": "Flight price module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1096/faulty/m0_adopt_review.func.abap b/tasks_gen/train/G1096/faulty/m0_adopt_review.func.abap new file mode 100644 index 0000000..2781fb5 --- /dev/null +++ b/tasks_gen/train/G1096/faulty/m0_adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' OR iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets > 0. + lv_deposit = lv_deposit + 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden = abap_true. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/faulty/m1_adopt_review.func.abap b/tasks_gen/train/G1096/faulty/m1_adopt_review.func.abap new file mode 100644 index 0000000..47d4620 --- /dev/null +++ b/tasks_gen/train/G1096/faulty/m1_adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' AND iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets <= 0. + lv_deposit = lv_deposit + 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden = abap_true. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/faulty/m2_adopt_review.func.abap b/tasks_gen/train/G1096/faulty/m2_adopt_review.func.abap new file mode 100644 index 0000000..b8ba07a --- /dev/null +++ b/tasks_gen/train/G1096/faulty/m2_adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' AND iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets > 0. + lv_deposit = lv_deposit - 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden = abap_true. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/faulty/m3_adopt_review.func.abap b/tasks_gen/train/G1096/faulty/m3_adopt_review.func.abap new file mode 100644 index 0000000..9bc3aab --- /dev/null +++ b/tasks_gen/train/G1096/faulty/m3_adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' AND iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets > 0. + lv_deposit = lv_deposit + 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden <> abap_true. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/faulty/m4_adopt_review.func.abap b/tasks_gen/train/G1096/faulty/m4_adopt_review.func.abap new file mode 100644 index 0000000..7d9c437 --- /dev/null +++ b/tasks_gen/train/G1096/faulty/m4_adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' AND iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets > 0. + lv_deposit = lv_deposit + 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden = abap_false. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/generation.json b/tasks_gen/train/G1096/generation.json new file mode 100644 index 0000000..180209f --- /dev/null +++ b/tasks_gen/train/G1096/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1096", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0127 (similarity spec 0.67, rules 0.60, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1096/hidden/adopt_hidden.clas.abap b/tasks_gen/train/G1096/hidden/adopt_hidden.clas.abap new file mode 100644 index 0000000..f3fc5c1 --- /dev/null +++ b/tasks_gen/train/G1096/hidden/adopt_hidden.clas.abap @@ -0,0 +1,131 @@ +CLASS {{p}}adopt_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES: BEGIN OF ty_result, + deposit TYPE decfloat34, + visit_day TYPE string, + reason TYPE string, + END OF ty_result. + + METHODS review + IMPORTING iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + METHODS dog_deposit FOR TESTING. + METHODS cat_deposit FOR TESTING. + METHODS rabbit_deposit FOR TESTING. + METHODS pet_surcharge FOR TESTING. + METHODS pet_limit FOR TESTING. + METHODS dog_flat_no_garden FOR TESTING. + METHODS too_young FOR TESTING. + METHODS age_limit FOR TESTING. + METHODS unknown_species FOR TESTING. + METHODS check_order FOR TESTING. + METHODS rejected_unchanged FOR TESTING. +ENDCLASS. + + +CLASS {{p}}adopt_hidden IMPLEMENTATION. + METHOD review. + TRY. + CALL FUNCTION '{{P}}ADOPT_REVIEW' + EXPORTING + iv_species = iv_species + iv_age_years = iv_age_years + iv_housing = iv_housing + iv_garden = iv_garden + iv_pets = iv_pets + IMPORTING + ev_deposit = rs_result-deposit + ev_visit_day = rs_result-visit_day. + CATCH {{p}}cx_adopt_reject INTO DATA(lo_error). + rs_result-reason = lo_error->mv_reason. + ENDTRY. + ENDMETHOD. + + METHOD dog_deposit. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `HOUSE` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = ls_result-deposit ). + cl_abap_unit_assert=>assert_equals( exp = `WED` act = ls_result-visit_day ). + cl_abap_unit_assert=>assert_initial( ls_result-reason ). + ENDMETHOD. + + METHOD cat_deposit. + DATA(ls_result) = review( iv_species = `CAT` iv_age_years = 25 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 80 ) act = ls_result-deposit ). + cl_abap_unit_assert=>assert_equals( exp = `WED` act = ls_result-visit_day ). + ENDMETHOD. + + METHOD rabbit_deposit. + DATA(ls_result) = review( iv_species = `RABBIT` iv_age_years = 20 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 40 ) act = ls_result-deposit ). + cl_abap_unit_assert=>assert_equals( exp = `WED` act = ls_result-visit_day ). + ENDMETHOD. + + METHOD pet_surcharge. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 40 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 162 ) act = ls_result-deposit ). + cl_abap_unit_assert=>assert_equals( exp = `SAT` act = ls_result-visit_day ). + ENDMETHOD. + + METHOD pet_limit. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 3 ). + cl_abap_unit_assert=>assert_equals( exp = `TOO_MANY_PETS` act = ls_result-reason ). + cl_abap_unit_assert=>assert_initial( ls_result-deposit ). + ENDMETHOD. + + METHOD dog_flat_no_garden. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `NO_GARDEN` act = ls_result-reason ). + ENDMETHOD. + + METHOD too_young. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 17 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `TOO_YOUNG` act = ls_result-reason ). + ENDMETHOD. + + METHOD age_limit. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 18 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_initial( ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = ls_result-deposit ). + cl_abap_unit_assert=>assert_equals( exp = `SAT` act = ls_result-visit_day ). + ENDMETHOD. + + METHOD unknown_species. + DATA(ls_result) = review( iv_species = `BIRD` iv_age_years = 30 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `UNKNOWN_SPECIES` act = ls_result-reason ). + ENDMETHOD. + + METHOD check_order. + DATA(ls_first) = review( iv_species = `BIRD` iv_age_years = 17 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `TOO_YOUNG` act = ls_first-reason ). + DATA(ls_second) = review( iv_species = `BIRD` iv_age_years = 30 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 5 ). + cl_abap_unit_assert=>assert_equals( exp = `UNKNOWN_SPECIES` act = ls_second-reason ). + DATA(ls_third) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 5 ). + cl_abap_unit_assert=>assert_equals( exp = `NO_GARDEN` act = ls_third-reason ). + ENDMETHOD. + + METHOD rejected_unchanged. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `NO_GARDEN` act = ls_result-reason ). + cl_abap_unit_assert=>assert_initial( ls_result-deposit ). + cl_abap_unit_assert=>assert_initial( ls_result-visit_day ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1096/mutation.json b/tasks_gen/train/G1096/mutation.json new file mode 100644 index 0000000..ab4da26 --- /dev/null +++ b/tasks_gen/train/G1096/mutation.json @@ -0,0 +1,68 @@ +{ + "task": "G1096", + "mutants": [ + { + "object": "{{P}}ADOPT_REVIEW", + "mutant": "line 30: AND -> OR (logic)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "CAT_DEPOSIT", + "DOG_DEPOSIT", + "RABBIT_DEPOSIT" + ] + }, + { + "object": "{{P}}ADOPT_REVIEW", + "mutant": "line 38: > -> <= (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "AGE_LIMIT", + "CAT_DEPOSIT", + "DOG_DEPOSIT", + "PET_SURCHARGE", + "RABBIT_DEPOSIT" + ] + }, + { + "object": "{{P}}ADOPT_REVIEW", + "mutant": "line 39: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "PET_SURCHARGE" + ] + }, + { + "object": "{{P}}ADOPT_REVIEW", + "mutant": "line 43: = -> <> (eq)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "AGE_LIMIT", + "CAT_DEPOSIT", + "DOG_DEPOSIT", + "PET_SURCHARGE", + "RABBIT_DEPOSIT" + ] + }, + { + "object": "{{P}}ADOPT_REVIEW", + "mutant": "line 43: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "AGE_LIMIT", + "CAT_DEPOSIT", + "DOG_DEPOSIT", + "PET_SURCHARGE", + "RABBIT_DEPOSIT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1096/reference/adopt_review.func.abap b/tasks_gen/train/G1096/reference/adopt_review.func.abap new file mode 100644 index 0000000..666162d --- /dev/null +++ b/tasks_gen/train/G1096/reference/adopt_review.func.abap @@ -0,0 +1,49 @@ +FUNCTION {{p}}adopt_review + IMPORTING + iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + EXPORTING + ev_deposit TYPE decfloat34 + ev_visit_day TYPE string + RAISING + {{p}}cx_adopt_reject. + + IF iv_age_years < 18. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_YOUNG' ). + ENDIF. + + DATA lv_deposit TYPE decfloat34. + CASE iv_species. + WHEN 'DOG'. + lv_deposit = 150. + WHEN 'CAT'. + lv_deposit = 80. + WHEN 'RABBIT'. + lv_deposit = 40. + WHEN OTHERS. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'UNKNOWN_SPECIES' ). + ENDCASE. + + IF iv_species = 'DOG' AND iv_housing = 'FLAT' AND iv_garden = abap_false. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'NO_GARDEN' ). + ENDIF. + + IF iv_pets > 2. + RAISE EXCEPTION NEW {{p}}cx_adopt_reject( iv_reason = 'TOO_MANY_PETS' ). + ENDIF. + + IF iv_pets > 0. + lv_deposit = lv_deposit + 12. + ENDIF. + ev_deposit = lv_deposit. + + IF iv_garden = abap_true. + ev_visit_day = 'SAT'. + ELSE. + ev_visit_day = 'WED'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/reference/adopt_test.clas.abap b/tasks_gen/train/G1096/reference/adopt_test.clas.abap new file mode 100644 index 0000000..c4691dc --- /dev/null +++ b/tasks_gen/train/G1096/reference/adopt_test.clas.abap @@ -0,0 +1,76 @@ +CLASS {{p}}adopt_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES: BEGIN OF ty_result, + deposit TYPE decfloat34, + visit_day TYPE string, + reason TYPE string, + END OF ty_result. + + METHODS review + IMPORTING iv_species TYPE string + iv_age_years TYPE i + iv_housing TYPE string + iv_garden TYPE abap_bool + iv_pets TYPE i + RETURNING VALUE(rs_result) TYPE ty_result. + + METHODS dog_deposit FOR TESTING. + METHODS surcharge FOR TESTING. + METHODS visit_day FOR TESTING. + METHODS too_young FOR TESTING. + METHODS no_garden FOR TESTING. +ENDCLASS. + + +CLASS {{p}}adopt_test IMPLEMENTATION. + METHOD review. + TRY. + CALL FUNCTION '{{P}}ADOPT_REVIEW' + EXPORTING + iv_species = iv_species + iv_age_years = iv_age_years + iv_housing = iv_housing + iv_garden = iv_garden + iv_pets = iv_pets + IMPORTING + ev_deposit = rs_result-deposit + ev_visit_day = rs_result-visit_day. + CATCH {{p}}cx_adopt_reject INTO DATA(lo_error). + rs_result-reason = lo_error->mv_reason. + ENDTRY. + ENDMETHOD. + + METHOD dog_deposit. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `HOUSE` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = ls_result-deposit ). + ENDMETHOD. + + METHOD surcharge. + DATA(ls_result) = review( iv_species = `CAT` iv_age_years = 25 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 1 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 92 ) act = ls_result-deposit ). + ENDMETHOD. + + METHOD visit_day. + DATA(ls_sat) = review( iv_species = `RABBIT` iv_age_years = 20 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `SAT` act = ls_sat-visit_day ). + DATA(ls_wed) = review( iv_species = `RABBIT` iv_age_years = 20 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `WED` act = ls_wed-visit_day ). + ENDMETHOD. + + METHOD too_young. + DATA(ls_result) = review( iv_species = `CAT` iv_age_years = 17 iv_housing = `HOUSE` + iv_garden = abap_true iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `TOO_YOUNG` act = ls_result-reason ). + ENDMETHOD. + + METHOD no_garden. + DATA(ls_result) = review( iv_species = `DOG` iv_age_years = 30 iv_housing = `FLAT` + iv_garden = abap_false iv_pets = 0 ). + cl_abap_unit_assert=>assert_equals( exp = `NO_GARDEN` act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1096/reference/cx_adopt_reject.clas.abap b/tasks_gen/train/G1096/reference/cx_adopt_reject.clas.abap new file mode 100644 index 0000000..1543a24 --- /dev/null +++ b/tasks_gen/train/G1096/reference/cx_adopt_reject.clas.abap @@ -0,0 +1,21 @@ +CLASS {{p}}cx_adopt_reject DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_reason TYPE string READ-ONLY. + + METHODS constructor + IMPORTING + iv_reason TYPE string. +ENDCLASS. + + +CLASS {{p}}cx_adopt_reject IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1096/seed/shelter_sample.func.abap b/tasks_gen/train/G1096/seed/shelter_sample.func.abap new file mode 100644 index 0000000..70c21d4 --- /dev/null +++ b/tasks_gen/train/G1096/seed/shelter_sample.func.abap @@ -0,0 +1,12 @@ +FUNCTION {{p}}shelter_sample + IMPORTING + iv_species TYPE string + EXPORTING + ev_slots TYPE i. + + ev_slots = 0. + IF iv_species IS NOT INITIAL. + ev_slots = 1. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1096/spec.md b/tasks_gen/train/G1096/spec.md new file mode 100644 index 0000000..db547e0 --- /dev/null +++ b/tasks_gen/train/G1096/spec.md @@ -0,0 +1,65 @@ +# 1. Goal +An animal shelter checks an adoption application before it approves the +adoption. The shelter office calls one function module for each application. +The function module calculates the deposit and the visit day. If the shelter +rejects the application, the function module raises a class-based exception. +The exception carries the reason of the rejection. + +# 2. Open questions +None. + +# 3. Context +- The function group {{P}}FG_SHELTER exists in package $TMP. +- The function module {{P}}SHELTER_SAMPLE exists in this group. It is a sample + module. Do not change it. +- The shelter office calls {{P}}ADOPT_REVIEW for each application. +- The shelter office catches the class-based exception {{P}}CX_ADOPT_REJECT + and reads the reason of the rejection from the attribute MV_REASON. + +# 4. Contract +Create the global exception class {{P}}CX_ADOPT_REJECT in package $TMP. +- It inherits from CX_STATIC_CHECK. +- It has the public read-only attribute MV_REASON of type STRING. +- It has a constructor with the importing parameter IV_REASON of type STRING. + The constructor stores the value of IV_REASON in MV_REASON. + +Create the function module {{P}}ADOPT_REVIEW in function group {{P}}FG_SHELTER +with this interface: +- IMPORTING iv_species TYPE string +- IMPORTING iv_age_years TYPE i +- IMPORTING iv_housing TYPE string +- IMPORTING iv_garden TYPE abap_bool +- IMPORTING iv_pets TYPE i +- EXPORTING ev_deposit TYPE decfloat34 +- EXPORTING ev_visit_day TYPE string +The function module raises the class-based exception {{P}}CX_ADOPT_REJECT. + +# 5. Business rules +1. Check the rules 2 to 5 in this order. Raise the exception with the given + reason as soon as one check fails. Do not change EV_DEPOSIT or EV_VISIT_DAY + in this case. +2. If IV_AGE_YEARS is less than 18, the reason is 'TOO_YOUNG'. +3. If IV_SPECIES is not 'DOG', not 'CAT' and not 'RABBIT', the reason is + 'UNKNOWN_SPECIES'. +4. If IV_SPECIES is 'DOG' and IV_HOUSING is 'FLAT' and IV_GARDEN is false, the + reason is 'NO_GARDEN'. +5. If IV_PETS is greater than 2, the reason is 'TOO_MANY_PETS'. +6. The deposit is 150.00 for 'DOG', 80.00 for 'CAT' and 40.00 for 'RABBIT'. +7. If IV_PETS is greater than 0, add 12.00 to the deposit. +8. The visit day is 'SAT' if IV_GARDEN is true. The visit day is 'WED' if + IV_GARDEN is false. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816, ABAP Platform 2025). +- Package $TMP. Do not use a transport. +- Use class-based exceptions. Do not use classic exceptions. Do not use + sy-subrc for the result of a check. +- Coding standard: Clean ABAP. +- Out of scope: do not change the function group {{P}}FG_SHELTER and the + function module {{P}}SHELTER_SAMPLE. + +# 7. Acceptance +- The exception class {{P}}CX_ADOPT_REJECT and the function module + {{P}}ADOPT_REVIEW are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1096/task.json b/tasks_gen/train/G1096/task.json new file mode 100644 index 0000000..5916687 --- /dev/null +++ b/tasks_gen/train/G1096/task.json @@ -0,0 +1,104 @@ +{ + "id": "G1096", + "category": "D", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_SHELTER", + "description": "Function group of the animal shelter" + }, + { + "type": "FUNC", + "name": "{{P}}SHELTER_SAMPLE", + "functionGroup": "{{P}}FG_SHELTER", + "file": "seed/shelter_sample.func.abap", + "description": "Sample module of the shelter" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_ADOPT_REJECT", + "description": "Class-based exception that carries the rejection reason in MV_REASON" + }, + { + "type": "FUNC", + "name": "{{P}}ADOPT_REVIEW", + "functionGroup": "{{P}}FG_SHELTER", + "params": [ + { + "name": "iv_species", + "type": "string" + }, + { + "name": "iv_age_years", + "type": "i" + }, + { + "name": "iv_housing", + "type": "string" + }, + { + "name": "iv_garden", + "type": "abap_bool" + }, + { + "name": "iv_pets", + "type": "i" + }, + { + "name": "ev_deposit", + "type": "decfloat34" + }, + { + "name": "ev_visit_day", + "type": "string" + } + ] + } + ], + "out_of_scope": [ + "{{P}}FG_SHELTER", + "{{P}}SHELTER_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}ADOPT_HIDDEN", + "file": "hidden/adopt_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_ADOPT_REJECT", + "file": "reference/cx_adopt_reject.clas.abap", + "description": "Class-based exception with the rejection reason" + }, + { + "type": "FUNC", + "name": "{{P}}ADOPT_REVIEW", + "functionGroup": "{{P}}FG_SHELTER", + "file": "reference/adopt_review.func.abap", + "description": "Adoption review module" + }, + { + "type": "CLAS", + "name": "{{P}}ADOPT_TEST", + "file": "reference/adopt_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1097/faulty/m0_weld_fee.func.abap b/tasks_gen/train/G1097/faulty/m0_weld_fee.func.abap new file mode 100644 index 0000000..1d913e6 --- /dev/null +++ b/tasks_gen/train/G1097/faulty/m0_weld_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}weld_fee + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + IF iv_seam_cm >= 0. + RAISE invalid_length. + ENDIF. + + ev_skip = abap_false. + + DATA lv_rate TYPE decfloat34. + CASE iv_material. + WHEN 'ST'. + lv_rate = 12. + WHEN 'SS'. + lv_rate = 20. + WHEN 'AL'. + IF iv_seam_cm > 50. + ev_skip = abap_true. + RETURN. + ENDIF. + lv_rate = 28. + WHEN OTHERS. + ev_skip = abap_true. + RETURN. + ENDCASE. + + DATA lv_fee TYPE decfloat34. + lv_fee = round( val = iv_seam_cm * lv_rate dec = 2 mode = cl_abap_math=>round_half_up ). + + IF iv_seam_cm > 200. + lv_fee = round( val = lv_fee * 9 / 10 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF iv_express = abap_true. + lv_fee = round( val = lv_fee * 125 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF lv_fee < 40. + lv_fee = 40. + ENDIF. + + cv_fee = lv_fee. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/faulty/m1_weld_fee.func.abap b/tasks_gen/train/G1097/faulty/m1_weld_fee.func.abap new file mode 100644 index 0000000..eeb6eb3 --- /dev/null +++ b/tasks_gen/train/G1097/faulty/m1_weld_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}weld_fee + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + IF iv_seam_cm < 0. + RAISE invalid_length. + ENDIF. + + ev_skip = abap_false. + + DATA lv_rate TYPE decfloat34. + CASE iv_material. + WHEN 'ST'. + lv_rate = 12. + WHEN 'SS'. + lv_rate = 20. + WHEN 'AL'. + IF iv_seam_cm > 50. + ev_skip = abap_true. + RETURN. + ENDIF. + lv_rate = 28. + WHEN OTHERS. + ev_skip = abap_false. + RETURN. + ENDCASE. + + DATA lv_fee TYPE decfloat34. + lv_fee = round( val = iv_seam_cm * lv_rate dec = 2 mode = cl_abap_math=>round_half_up ). + + IF iv_seam_cm > 200. + lv_fee = round( val = lv_fee * 9 / 10 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF iv_express = abap_true. + lv_fee = round( val = lv_fee * 125 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF lv_fee < 40. + lv_fee = 40. + ENDIF. + + cv_fee = lv_fee. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/faulty/m2_weld_fee.func.abap b/tasks_gen/train/G1097/faulty/m2_weld_fee.func.abap new file mode 100644 index 0000000..3884b1f --- /dev/null +++ b/tasks_gen/train/G1097/faulty/m2_weld_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}weld_fee + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + IF iv_seam_cm < 0. + RAISE invalid_length. + ENDIF. + + ev_skip = abap_false. + + DATA lv_rate TYPE decfloat34. + CASE iv_material. + WHEN 'ST'. + lv_rate = 12. + WHEN 'SS'. + lv_rate = 20. + WHEN 'AL'. + IF iv_seam_cm > 50. + ev_skip = abap_true. + RETURN. + ENDIF. + lv_rate = 28. + WHEN OTHERS. + ev_skip = abap_true. + RETURN. + ENDCASE. + + DATA lv_fee TYPE decfloat34. + lv_fee = round( val = iv_seam_cm / lv_rate dec = 2 mode = cl_abap_math=>round_half_up ). + + IF iv_seam_cm > 200. + lv_fee = round( val = lv_fee * 9 / 10 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF iv_express = abap_true. + lv_fee = round( val = lv_fee * 125 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF lv_fee < 40. + lv_fee = 40. + ENDIF. + + cv_fee = lv_fee. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/faulty/m4_weld_fee.func.abap b/tasks_gen/train/G1097/faulty/m4_weld_fee.func.abap new file mode 100644 index 0000000..46dea4b --- /dev/null +++ b/tasks_gen/train/G1097/faulty/m4_weld_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}weld_fee + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + IF iv_seam_cm < 0. + RAISE invalid_length. + ENDIF. + + ev_skip = abap_false. + + DATA lv_rate TYPE decfloat34. + CASE iv_material. + WHEN 'ST'. + lv_rate = 12. + WHEN 'SS'. + lv_rate = 20. + WHEN 'AL'. + IF iv_seam_cm > 50. + ev_skip = abap_true. + RETURN. + ENDIF. + lv_rate = 28. + WHEN OTHERS. + ev_skip = abap_true. + RETURN. + ENDCASE. + + DATA lv_fee TYPE decfloat34. + lv_fee = round( val = iv_seam_cm * lv_rate dec = 2 mode = cl_abap_math=>round_half_up ). + + IF iv_seam_cm > 200. + lv_fee = round( val = lv_fee * 9 / 10 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF iv_express <> abap_true. + lv_fee = round( val = lv_fee * 125 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF lv_fee < 40. + lv_fee = 40. + ENDIF. + + cv_fee = lv_fee. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/generation.json b/tasks_gen/train/G1097/generation.json new file mode 100644 index 0000000..d9668dd --- /dev/null +++ b/tasks_gen/train/G1097/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1097", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1097/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1097/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..78aa328 --- /dev/null +++ b/tasks_gen/train/G1097/hidden/t14_hidden.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool DEFAULT abap_false + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS rejects_negative_len FOR TESTING. + METHODS steel_rate FOR TESTING. + METHODS stainless_rate FOR TESTING. + METHODS alu_short_charged FOR TESTING. + METHODS alu_long_skipped FOR TESTING. + METHODS unknown_mat_skipped FOR TESTING. + METHODS discount_over_200cm FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS rounding_two_decimals FOR TESTING. + METHODS skip_false_for_steel FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}WELD_FEE' + EXPORTING + iv_material = iv_material + iv_seam_cm = iv_seam_cm + iv_express = iv_express + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_length = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD rejects_negative_len. + call( EXPORTING iv_material = `ST` iv_seam_cm = -1 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD steel_rate. + call( EXPORTING iv_material = `ST` iv_seam_cm = 10 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD stainless_rate. + call( EXPORTING iv_material = `SS` iv_seam_cm = 10 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 200 ) act = lv_fee ). + ENDMETHOD. + + METHOD alu_short_charged. + call( EXPORTING iv_material = `AL` iv_seam_cm = 30 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 840 ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD alu_long_skipped. + call( EXPORTING iv_material = `AL` iv_seam_cm = 60 iv_fee = 7 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_fee ). + ENDMETHOD. + + METHOD unknown_mat_skipped. + call( EXPORTING iv_material = `TI` iv_seam_cm = 10 iv_fee = 5 + IMPORTING ev_fee = DATA(lv_fee) ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = lv_fee ). + ENDMETHOD. + + METHOD discount_over_200cm. + call( EXPORTING iv_material = `ST` iv_seam_cm = 250 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 2700 ) act = lv_fee ). + ENDMETHOD. + + METHOD express_surcharge. + call( EXPORTING iv_material = `ST` iv_seam_cm = 10 iv_express = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = lv_fee ). + ENDMETHOD. + + METHOD minimum_charge. + call( EXPORTING iv_material = `ST` iv_seam_cm = 2 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 40 ) act = lv_fee ). + ENDMETHOD. + + METHOD rounding_two_decimals. + call( EXPORTING iv_material = `ST` iv_seam_cm = '10.00125' + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '120.02' ) act = lv_fee ). + ENDMETHOD. + + METHOD skip_false_for_steel. + call( EXPORTING iv_material = `ST` iv_seam_cm = 10 + IMPORTING ev_skip = DATA(lv_skip) ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1097/mutation.json b/tasks_gen/train/G1097/mutation.json new file mode 100644 index 0000000..def6041 --- /dev/null +++ b/tasks_gen/train/G1097/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1097", + "mutants": [ + { + "object": "{{P}}WELD_FEE", + "mutant": "line 13: < -> >= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "ALU_LONG_SKIPPED", + "ALU_SHORT_CHARGED", + "DISCOUNT_OVER_200CM", + "EXPRESS_SURCHARGE", + "MINIMUM_CHARGE", + "REJECTS_NEGATIVE_LEN", + "ROUNDING_TWO_DECIMALS", + "STAINLESS_RATE", + "STEEL_RATE", + "UNKNOWN_MAT_SKIPPED" + ] + }, + { + "object": "{{P}}WELD_FEE", + "mutant": "line 32: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "UNKNOWN_MAT_SKIPPED" + ] + }, + { + "object": "{{P}}WELD_FEE", + "mutant": "line 37: * -> / (arith)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "ALU_SHORT_CHARGED", + "DISCOUNT_OVER_200CM", + "EXPRESS_SURCHARGE", + "ROUNDING_TWO_DECIMALS", + "STAINLESS_RATE", + "STEEL_RATE" + ] + }, + { + "object": "{{P}}WELD_FEE", + "mutant": "line 40: 2 -> 3 (const)", + "status": "survived", + "hidden": "11/11", + "failed_tests": [] + }, + { + "object": "{{P}}WELD_FEE", + "mutant": "line 43: = -> <> (eq)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "ALU_SHORT_CHARGED", + "DISCOUNT_OVER_200CM", + "EXPRESS_SURCHARGE", + "ROUNDING_TWO_DECIMALS", + "STAINLESS_RATE", + "STEEL_RATE" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1097/reference/t14_test.clas.abap b/tasks_gen/train/G1097/reference/t14_test.clas.abap new file mode 100644 index 0000000..3f87c7c --- /dev/null +++ b/tasks_gen/train/G1097/reference/t14_test.clas.abap @@ -0,0 +1,61 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool DEFAULT abap_false + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS steel_charge FOR TESTING. + METHODS stainless_charge FOR TESTING. + METHODS aluminium_charge FOR TESTING. + METHODS express_and_discount FOR TESTING. + METHODS minimum_charge FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}WELD_FEE' + EXPORTING + iv_material = iv_material + iv_seam_cm = iv_seam_cm + iv_express = iv_express + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_length = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD steel_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 120 ) + act = fee_for( iv_material = `ST` iv_seam_cm = 10 ) ). + ENDMETHOD. + + METHOD stainless_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 200 ) + act = fee_for( iv_material = `SS` iv_seam_cm = 10 ) ). + ENDMETHOD. + + METHOD aluminium_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 840 ) + act = fee_for( iv_material = `AL` iv_seam_cm = 30 ) ). + ENDMETHOD. + + METHOD express_and_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 3375 ) + act = fee_for( iv_material = `ST` iv_seam_cm = 250 iv_express = abap_true ) ). + ENDMETHOD. + + METHOD minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 40 ) + act = fee_for( iv_material = `ST` iv_seam_cm = 2 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1097/reference/weld_fee.func.abap b/tasks_gen/train/G1097/reference/weld_fee.func.abap new file mode 100644 index 0000000..6c9945c --- /dev/null +++ b/tasks_gen/train/G1097/reference/weld_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}weld_fee + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + IF iv_seam_cm < 0. + RAISE invalid_length. + ENDIF. + + ev_skip = abap_false. + + DATA lv_rate TYPE decfloat34. + CASE iv_material. + WHEN 'ST'. + lv_rate = 12. + WHEN 'SS'. + lv_rate = 20. + WHEN 'AL'. + IF iv_seam_cm > 50. + ev_skip = abap_true. + RETURN. + ENDIF. + lv_rate = 28. + WHEN OTHERS. + ev_skip = abap_true. + RETURN. + ENDCASE. + + DATA lv_fee TYPE decfloat34. + lv_fee = round( val = iv_seam_cm * lv_rate dec = 2 mode = cl_abap_math=>round_half_up ). + + IF iv_seam_cm > 200. + lv_fee = round( val = lv_fee * 9 / 10 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF iv_express = abap_true. + lv_fee = round( val = lv_fee * 125 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + IF lv_fee < 40. + lv_fee = 40. + ENDIF. + + cv_fee = lv_fee. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/seed/weld_sample.func.abap b/tasks_gen/train/G1097/seed/weld_sample.func.abap new file mode 100644 index 0000000..5185c80 --- /dev/null +++ b/tasks_gen/train/G1097/seed/weld_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}weld_sample + IMPORTING + iv_material TYPE string + iv_seam_cm TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_length. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1097/spec.md b/tasks_gen/train/G1097/spec.md new file mode 100644 index 0000000..0ff22a6 --- /dev/null +++ b/tasks_gen/train/G1097/spec.md @@ -0,0 +1,48 @@ +# 1. Goal +A mobile welding shop repairs metal parts. For each weld seam of a repair order, +the order desk calls one event module. The event module calculates the charge of +the seam. The event module must have the same interface as the sample event +module, because the order desk calls all event modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_WELD exists in package $TMP. +- Function module {{P}}WELD_SAMPLE exists in this group. It is the sample event + module. Its interface is the interface that all event modules must use. + +# 4. Contract +- Create the function module {{P}}WELD_FEE in function group {{P}}FG_WELD. +- Use exactly the interface of {{P}}WELD_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the seam length is less than 0, raise the exception INVALID_LENGTH. +2. Set the skip flag to false. +3. Material ST (steel): the rate is 12.00 per centimetre. +4. Material SS (stainless steel): the rate is 20.00 per centimetre. +5. Material AL (aluminium): the rate is 28.00 per centimetre. The shop welds an + aluminium seam only if the seam is at most 50 cm long. +6. If the material is AL and the seam is longer than 50 cm, set the skip flag to + true and stop. Do not change the charge. +7. For all other materials, set the skip flag to true and stop. Do not change the + charge. +8. The charge is the rate multiplied by the seam length, rounded half up to 2 + decimals. +9. If the seam is longer than 200 cm, the charge gets a discount of 10 %. Round + the result half up to 2 decimals. +10. If the express flag is true, the charge gets a surcharge of 25 %. Round the + result half up to 2 decimals. +11. Apply rule 9 before rule 10. +12. The minimum charge is 40.00. If the charge is less than 40.00, use 40.00. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}WELD_SAMPLE. + +# 7. Acceptance +- The function module {{P}}WELD_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1097/task.json b/tasks_gen/train/G1097/task.json new file mode 100644 index 0000000..c8dafbd --- /dev/null +++ b/tasks_gen/train/G1097/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1097", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_WELD", + "description": "Welding charge events" + }, + { + "type": "FUNC", + "name": "{{P}}WELD_SAMPLE", + "functionGroup": "{{P}}FG_WELD", + "file": "seed/weld_sample.func.abap", + "description": "Sample event module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}WELD_FEE", + "functionGroup": "{{P}}FG_WELD", + "params": [ + { + "name": "iv_material", + "type": "string" + }, + { + "name": "iv_seam_cm", + "type": "decfloat34" + }, + { + "name": "iv_express", + "type": "abap_bool" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}WELD_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}WELD_FEE", + "functionGroup": "{{P}}FG_WELD", + "file": "reference/weld_fee.func.abap", + "description": "Welding charge event module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1098/faulty/m0_drv_fee.func.abap b/tasks_gen/train/G1098/faulty/m0_drv_fee.func.abap new file mode 100644 index 0000000..d059422 --- /dev/null +++ b/tasks_gen/train/G1098/faulty/m0_drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_true. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee * ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 2 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes > 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/faulty/m1_drv_fee.func.abap b/tasks_gen/train/G1098/faulty/m1_drv_fee.func.abap new file mode 100644 index 0000000..3036768 --- /dev/null +++ b/tasks_gen/train/G1098/faulty/m1_drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_false. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate / iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee * ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 2 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes > 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/faulty/m2_drv_fee.func.abap b/tasks_gen/train/G1098/faulty/m2_drv_fee.func.abap new file mode 100644 index 0000000..ecc435d --- /dev/null +++ b/tasks_gen/train/G1098/faulty/m2_drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_false. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee / ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 2 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes > 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/faulty/m3_drv_fee.func.abap b/tasks_gen/train/G1098/faulty/m3_drv_fee.func.abap new file mode 100644 index 0000000..9b06ea6 --- /dev/null +++ b/tasks_gen/train/G1098/faulty/m3_drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_false. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee * ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 3 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes > 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/faulty/m4_drv_fee.func.abap b/tasks_gen/train/G1098/faulty/m4_drv_fee.func.abap new file mode 100644 index 0000000..d7a81f7 --- /dev/null +++ b/tasks_gen/train/G1098/faulty/m4_drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_false. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee * ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 2 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes <= 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/generation.json b/tasks_gen/train/G1098/generation.json new file mode 100644 index 0000000..b5e6217 --- /dev/null +++ b/tasks_gen/train/G1098/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1098", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1098/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1098/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..f3b5ee8 --- /dev/null +++ b/tasks_gen/train/G1098/hidden/t41_hidden.clas.abap @@ -0,0 +1,138 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING ev_block TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS car_rate FOR TESTING. + METHODS truck_rate FOR TESTING. + METHODS moto_rate FOR TESTING. + METHODS night_surcharge FOR TESTING. + METHODS motorway_surcharge FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS unknown_vehicle FOR TESTING. + METHODS block_long_lesson FOR TESTING. + METHODS no_block_normal_lesson FOR TESTING. + METHODS invalid_minutes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + METHOD call. + CLEAR ev_fee. + CALL FUNCTION '{{P}}DRV_FEE' + EXPORTING + iv_lesson_type = iv_lesson_type + iv_vehicle = iv_vehicle + iv_minutes = iv_minutes + IMPORTING + ev_block = ev_block + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_minutes = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD car_rate. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '72.00'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'CAR' iv_minutes = 60 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD truck_rate. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '75.00'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'TRUCK' iv_minutes = 30 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD moto_rate. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '54.00'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'MOTORCYCLE' iv_minutes = 60 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD night_surcharge. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '90.00'. + call( EXPORTING iv_lesson_type = 'NIGHT' iv_vehicle = 'CAR' iv_minutes = 60 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD motorway_surcharge. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '105.00'. + call( EXPORTING iv_lesson_type = 'MOTORWAY' iv_vehicle = 'TRUCK' iv_minutes = 30 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD rounding_half_up. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '21.88'. + call( EXPORTING iv_lesson_type = 'NIGHT' iv_vehicle = 'TRUCK' iv_minutes = 7 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '15.00'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'MOTORCYCLE' iv_minutes = 10 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD unknown_vehicle. + DATA lv_fee TYPE decfloat34. + DATA lv_block TYPE abap_bool. + DATA lv_rc TYPE sysubrc. + DATA lv_exp TYPE decfloat34. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'BOAT' iv_minutes = 200 + IMPORTING ev_fee = lv_fee ev_block = lv_block ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_block ). + ENDMETHOD. + + METHOD block_long_lesson. + DATA lv_fee TYPE decfloat34. + DATA lv_block TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '145.20'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'CAR' iv_minutes = 121 + IMPORTING ev_fee = lv_fee ev_block = lv_block ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_block ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD no_block_normal_lesson. + DATA lv_fee TYPE decfloat34. + DATA lv_block TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '144.00'. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'CAR' iv_minutes = 120 + IMPORTING ev_fee = lv_fee ev_block = lv_block ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_block ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD invalid_minutes. + DATA lv_fee TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lesson_type = 'NORMAL' iv_vehicle = 'CAR' iv_minutes = 0 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1098/mutation.json b/tasks_gen/train/G1098/mutation.json new file mode 100644 index 0000000..60e4079 --- /dev/null +++ b/tasks_gen/train/G1098/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1098", + "mutants": [ + { + "object": "{{P}}DRV_FEE", + "mutant": "line 16: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "NO_BLOCK_NORMAL_LESSON", + "UNKNOWN_VEHICLE" + ] + }, + { + "object": "{{P}}DRV_FEE", + "mutant": "line 34: * -> / (arith)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "BLOCK_LONG_LESSON", + "CAR_RATE", + "MOTORWAY_SURCHARGE", + "MOTO_RATE", + "NIGHT_SURCHARGE", + "NO_BLOCK_NORMAL_LESSON", + "ROUNDING_HALF_UP", + "TRUCK_RATE" + ] + }, + { + "object": "{{P}}DRV_FEE", + "mutant": "line 44: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "MOTORWAY_SURCHARGE", + "NIGHT_SURCHARGE", + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}DRV_FEE", + "mutant": "line 46: 2 -> 3 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}DRV_FEE", + "mutant": "line 52: > -> <= (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BLOCK_LONG_LESSON", + "NO_BLOCK_NORMAL_LESSON" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1098/reference/drv_fee.func.abap b/tasks_gen/train/G1098/reference/drv_fee.func.abap new file mode 100644 index 0000000..cd84421 --- /dev/null +++ b/tasks_gen/train/G1098/reference/drv_fee.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}drv_fee + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + DATA lv_rate TYPE decfloat34. + DATA lv_surcharge TYPE decfloat34. + + ev_block = abap_false. + cv_fee = 0. + + IF iv_minutes < 1. + RAISE invalid_minutes. + ENDIF. + + CASE iv_vehicle. + WHEN 'CAR'. + lv_rate = '1.20'. + WHEN 'TRUCK'. + lv_rate = '2.50'. + WHEN 'MOTORCYCLE'. + lv_rate = '0.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_minutes. + + CASE iv_lesson_type. + WHEN 'NIGHT'. + lv_surcharge = '0.25'. + WHEN 'MOTORWAY'. + lv_surcharge = '0.40'. + WHEN OTHERS. + lv_surcharge = 0. + ENDCASE. + cv_fee = cv_fee * ( 1 + lv_surcharge ). + + cv_fee = round( val = cv_fee dec = 2 ). + + IF cv_fee < 15. + cv_fee = 15. + ENDIF. + + IF iv_minutes > 120. + ev_block = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/reference/t41_test.clas.abap b/tasks_gen/train/G1098/reference/t41_test.clas.abap new file mode 100644 index 0000000..65a18b2 --- /dev/null +++ b/tasks_gen/train/G1098/reference/t41_test.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS car_lesson FOR TESTING. + METHODS surcharges FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS block_flag FOR TESTING. + METHODS invalid_minutes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_block TYPE abap_bool. + CALL FUNCTION '{{P}}DRV_FEE' + EXPORTING + iv_lesson_type = iv_lesson_type + iv_vehicle = iv_vehicle + iv_minutes = iv_minutes + IMPORTING + ev_block = lv_block + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_minutes = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD car_lesson. + DATA lv_exp TYPE decfloat34 VALUE '72.00'. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_lesson_type = 'NORMAL' iv_vehicle = 'CAR' iv_minutes = 60 ) ). + ENDMETHOD. + + METHOD surcharges. + DATA lv_exp_night TYPE decfloat34 VALUE '90.00'. + DATA lv_exp_motorway TYPE decfloat34 VALUE '105.00'. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp_night + act = fee_for( iv_lesson_type = 'NIGHT' iv_vehicle = 'CAR' iv_minutes = 60 ) ). + cl_abap_unit_assert=>assert_equals( + exp = lv_exp_motorway + act = fee_for( iv_lesson_type = 'MOTORWAY' iv_vehicle = 'TRUCK' iv_minutes = 30 ) ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_exp TYPE decfloat34 VALUE '15.00'. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_lesson_type = 'NORMAL' iv_vehicle = 'MOTORCYCLE' iv_minutes = 10 ) ). + ENDMETHOD. + + METHOD block_flag. + DATA lv_block TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}DRV_FEE' + EXPORTING + iv_lesson_type = 'NORMAL' + iv_vehicle = 'CAR' + iv_minutes = 121 + IMPORTING + ev_block = lv_block + CHANGING + cv_fee = lv_fee. + cl_abap_unit_assert=>assert_true( lv_block ). + ENDMETHOD. + + METHOD invalid_minutes. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}DRV_FEE' + EXPORTING + iv_lesson_type = 'NORMAL' + iv_vehicle = 'CAR' + iv_minutes = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_minutes = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1098/seed/drv_sample.func.abap b/tasks_gen/train/G1098/seed/drv_sample.func.abap new file mode 100644 index 0000000..c0a2bd9 --- /dev/null +++ b/tasks_gen/train/G1098/seed/drv_sample.func.abap @@ -0,0 +1,16 @@ +FUNCTION {{p}}drv_sample + IMPORTING + iv_lesson_type TYPE string + iv_vehicle TYPE string + iv_minutes TYPE i + EXPORTING + ev_block TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_minutes. + + ev_block = abap_false. + cv_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1098/spec.md b/tasks_gen/train/G1098/spec.md new file mode 100644 index 0000000..38aedb8 --- /dev/null +++ b/tasks_gen/train/G1098/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +Calculate the fee of one driving lesson for a driving school. The lesson +booking program calls one function module for each completed lesson. The +function module must have the same interface as the sample lesson module, +because the booking program calls all lesson modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_DRIVE exists in package $TMP. +- Function module {{P}}DRV_SAMPLE exists in this group. It is the sample + lesson module. Its interface is the interface that all lesson modules must + use. + +# 4. Contract +- Create the function module {{P}}DRV_FEE in function group {{P}}FG_DRIVE. +- Use exactly the interface of {{P}}DRV_SAMPLE: the same parameter names, + kinds and types, and the same exceptions. + +# 5. Business rules +1. If iv_minutes is less than 1, raise the exception INVALID_MINUTES. This + rule applies before all other rules. +2. The rate of one lesson minute depends on the vehicle: CAR 1.20, TRUCK + 2.50, MOTORCYCLE 0.90. +3. If iv_vehicle is not one of these three values, the fee is 0 and the block + flag is false. Do not raise an exception. This rule also applies when + iv_minutes is more than 120. +4. The fee is the rate multiplied by iv_minutes. +5. The lesson type adds a surcharge to this amount: NIGHT 25 %, MOTORWAY + 40 %. All other lesson types have no surcharge. +6. Round the fee half up to 2 decimals. +7. The minimum fee of one lesson is 15.00. +8. Set the block flag to true if iv_minutes is more than 120. Set the block + flag to false in all other cases. + +# 6. Constraints +- Release target: 7.40 SP05. Use only syntax that this release supports. +- Coding standards: Clean ABAP. No ATC priority 1 or priority 2 findings. +- Out of scope: do not change {{P}}DRV_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1098/task.json b/tasks_gen/train/G1098/task.json new file mode 100644 index 0000000..260392d --- /dev/null +++ b/tasks_gen/train/G1098/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1098", + "category": "G", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v740sp05", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_DRIVE", + "description": "Driving lesson fee modules" + }, + { + "type": "FUNC", + "name": "{{P}}DRV_SAMPLE", + "functionGroup": "{{P}}FG_DRIVE", + "file": "seed/drv_sample.func.abap", + "description": "Sample lesson module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}DRV_FEE", + "functionGroup": "{{P}}FG_DRIVE", + "params": [ + { + "name": "iv_lesson_type", + "type": "string" + }, + { + "name": "iv_vehicle", + "type": "string" + }, + { + "name": "iv_minutes", + "type": "i" + }, + { + "name": "ev_block", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}DRV_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}DRV_FEE", + "functionGroup": "{{P}}FG_DRIVE", + "file": "reference/drv_fee.func.abap", + "description": "Lesson fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "T41 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1099/generation.json b/tasks_gen/train/G1099/generation.json new file mode 100644 index 0000000..59cb550 --- /dev/null +++ b/tasks_gen/train/G1099/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1099", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1099/reference/salon_pricer.clas.abap b/tasks_gen/train/G1099/reference/salon_pricer.clas.abap new file mode 100644 index 0000000..9c8f7e0 --- /dev/null +++ b/tasks_gen/train/G1099/reference/salon_pricer.clas.abap @@ -0,0 +1,38 @@ +CLASS {{p}}salon_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_salon_price. + + PRIVATE SECTION. + METHODS base_price + IMPORTING iv_service TYPE {{p}}if_salon_price=>ty_service + RETURNING VALUE(rv_price) TYPE {{p}}if_salon_price=>ty_amount. + METHODS minimum_price + IMPORTING iv_price TYPE {{p}}if_salon_price=>ty_amount + RETURNING VALUE(rv_price) TYPE {{p}}if_salon_price=>ty_amount. +ENDCLASS. + + +CLASS {{p}}salon_pricer IMPLEMENTATION. + METHOD {{p}}if_salon_price~price. + IF iv_service = {{p}}if_salon_price=>c_service-cut AND iv_child = abap_true. + rv_price = '12.00'. + RETURN. + ENDIF. + rv_price = minimum_price( base_price( iv_service ) ). + ENDMETHOD. + + METHOD base_price. + CASE iv_service. + WHEN {{p}}if_salon_price=>c_service-cut. + rv_price = '42.00'. + WHEN {{p}}if_salon_price=>c_service-colour. + rv_price = '90.00'. + WHEN {{p}}if_salon_price=>c_service-perm. + rv_price = '95.00'. + ENDCASE. + ENDMETHOD. + + METHOD minimum_price. + rv_price = nmax( val1 = iv_price val2 = '20.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1099/reference/salon_pricer.testclasses.abap b/tasks_gen/train/G1099/reference/salon_pricer.testclasses.abap new file mode 100644 index 0000000..290c688 --- /dev/null +++ b/tasks_gen/train/G1099/reference/salon_pricer.testclasses.abap @@ -0,0 +1,44 @@ +CLASS ltc_salon_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_salon_price. + METHODS setup. + METHODS cut_price FOR TESTING. + METHODS colour_price FOR TESTING. + METHODS perm_price FOR TESTING. + METHODS child_cut_price FOR TESTING. +ENDCLASS. + + +CLASS ltc_salon_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}salon_pricer( ). + ENDMETHOD. + + METHOD cut_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_salon_price=>ty_amount( '42.00' ) + act = mo_cut->price( iv_service = {{p}}if_salon_price=>c_service-cut + iv_child = abap_false ) ). + ENDMETHOD. + + METHOD colour_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_salon_price=>ty_amount( '90.00' ) + act = mo_cut->price( iv_service = {{p}}if_salon_price=>c_service-colour + iv_child = abap_false ) ). + ENDMETHOD. + + METHOD perm_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_salon_price=>ty_amount( '95.00' ) + act = mo_cut->price( iv_service = {{p}}if_salon_price=>c_service-perm + iv_child = abap_false ) ). + ENDMETHOD. + + METHOD child_cut_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_salon_price=>ty_amount( '12.00' ) + act = mo_cut->price( iv_service = {{p}}if_salon_price=>c_service-cut + iv_child = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1099/seed/if_salon_price.intf.abap b/tasks_gen/train/G1099/seed/if_salon_price.intf.abap new file mode 100644 index 0000000..b669b1f --- /dev/null +++ b/tasks_gen/train/G1099/seed/if_salon_price.intf.abap @@ -0,0 +1,14 @@ +INTERFACE {{p}}if_salon_price PUBLIC. + TYPES ty_service TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + CONSTANTS: + BEGIN OF c_service, + cut TYPE ty_service VALUE 'CUT', + colour TYPE ty_service VALUE 'COLOUR', + perm TYPE ty_service VALUE 'PERM', + END OF c_service. + METHODS price + IMPORTING iv_service TYPE ty_service + iv_child TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDINTERFACE. diff --git a/tasks_gen/train/G1099/spec.md b/tasks_gen/train/G1099/spec.md new file mode 100644 index 0000000..0b29b8e --- /dev/null +++ b/tasks_gen/train/G1099/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A hair salon prices appointments. The checkout program calls one class for each +appointment. The class returns the price of the appointment in euro. + +# 2. Open questions +None. + +# 3. Context +- Interface {{P}}IF_SALON_PRICE exists in package $TMP. It is active. +- The interface has the type TY_SERVICE (CHAR 10) and the type TY_AMOUNT + (P length 8, 2 decimals). +- The interface has the constant structure C_SERVICE with the components CUT, + COLOUR and PERM. +- The interface has one method: + PRICE IMPORTING iv_service TYPE ty_service + iv_child TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. +- iv_child is abap_true when the customer is under 12 years. + +# 4. Contract +- Create the class {{P}}SALON_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_SALON_PRICE. +- Do not add other public methods. + +# 5. Business rules +1. The price is in euro with two decimals. +2. A cut costs 42.00 EUR. +3. A colour service costs 78.00 EUR. A colour service includes a scalp + treatment. Add 12.00 EUR to the price of a colour service. +4. A perm costs 95.00 EUR. +5. A cut for a child under 12 years costs exactly 12.00 EUR. +6. The minimum price of an appointment is 20.00 EUR. + +# 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_SALON_PRICE. + +# 7. Acceptance +- The class is active and has no syntax error. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1099/task.json b/tasks_gen/train/G1099/task.json new file mode 100644 index 0000000..544f39f --- /dev/null +++ b/tasks_gen/train/G1099/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1099", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "Rule 5 fixes the price of a child's cut at exactly 12.00 EUR, but rule 6 sets the minimum price of an appointment to 20.00 EUR, so the price of a child's cut is not defined.", + "keywords": [ + [ + "child", + "12" + ], + [ + "minimum", + "20" + ], + [ + "child", + "cut" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_SALON_PRICE", + "file": "seed/if_salon_price.intf.abap", + "description": "Salon price contract: service type, amount type, service constants and the price method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SALON_PRICER", + "implements": "{{P}}IF_SALON_PRICE" + } + ], + "out_of_scope": [ + "{{P}}IF_SALON_PRICE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SALON_PRICER", + "file": "reference/salon_pricer.clas.abap", + "description": "Intended salon price class (the child cut price rule wins)", + "testclasses_file": "reference/salon_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1100/faulty/m0_i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/faulty/m0_i_berth_fee.ddls.asddls new file mode 100644 index 0000000..ee61a72 --- /dev/null +++ b/tasks_gen/train/G1100/faulty/m0_i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + left outer join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m < 8 then 'SMALL' + when berth.length_m <= 15 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee + berth.length_m * pier.rate_per_m + + case when berth.shore_power = 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/faulty/m1_i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/faulty/m1_i_berth_fee.ddls.asddls new file mode 100644 index 0000000..74ea223 --- /dev/null +++ b/tasks_gen/train/G1100/faulty/m1_i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + inner join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m >= 8 then 'SMALL' + when berth.length_m <= 15 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee + berth.length_m * pier.rate_per_m + + case when berth.shore_power = 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/faulty/m2_i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/faulty/m2_i_berth_fee.ddls.asddls new file mode 100644 index 0000000..25953f8 --- /dev/null +++ b/tasks_gen/train/G1100/faulty/m2_i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + inner join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m < 8 then 'SMALL' + when berth.length_m <= 16 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee + berth.length_m * pier.rate_per_m + + case when berth.shore_power = 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/faulty/m3_i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/faulty/m3_i_berth_fee.ddls.asddls new file mode 100644 index 0000000..25feaa1 --- /dev/null +++ b/tasks_gen/train/G1100/faulty/m3_i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + inner join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m < 8 then 'SMALL' + when berth.length_m <= 15 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee - berth.length_m * pier.rate_per_m + + case when berth.shore_power = 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/faulty/m4_i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/faulty/m4_i_berth_fee.ddls.asddls new file mode 100644 index 0000000..598c559 --- /dev/null +++ b/tasks_gen/train/G1100/faulty/m4_i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + inner join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m < 8 then 'SMALL' + when berth.length_m <= 15 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee + berth.length_m * pier.rate_per_m + + case when berth.shore_power <> 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/generation.json b/tasks_gen/train/G1100/generation.json new file mode 100644 index 0000000..3a77551 --- /dev/null +++ b/tasks_gen/train/G1100/generation.json @@ -0,0 +1,37 @@ +{ + "id": "G1100", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0005 (similarity spec 0.57, rules 0.64, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "seed/berth.tabl.asabap: field name days is a reserved word; choose another name" + ], + "by_check": { + "static": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1100/hidden/t42_hidden.clas.abap b/tasks_gen/train/G1100/hidden/t42_hidden.clas.abap new file mode 100644 index 0000000..1cc2cc1 --- /dev/null +++ b/tasks_gen/train/G1100/hidden/t42_hidden.clas.abap @@ -0,0 +1,129 @@ +CLASS {{p}}t42_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS small_class FOR TESTING. + METHODS medium_class FOR TESTING. + METHODS large_class FOR TESTING. + METHODS length_in_centimetres FOR TESTING. + METHODS fee_without_power FOR TESTING. + METHODS power_surcharge FOR TESTING. + METHODS rounds_after_days FOR TESTING. + METHODS large_boat_fee FOR TESTING. + METHODS unknown_pier_ignored FOR TESTING. + METHODS boat_name_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BERTH_FEE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_pier TYPE STANDARD TABLE OF {{p}}pier WITH EMPTY KEY. + DATA lt_berth TYPE STANDARD TABLE OF {{p}}berth WITH EMPTY KEY. + + go_env->clear_doubles( ). + + lt_pier = VALUE #( ( pier_code = 'A' base_fee = '20.00' rate_per_m = '3.00' power_fee = '5.00' ) + ( pier_code = 'B' base_fee = '10.00' rate_per_m = '1.55' power_fee = '2.00' ) + ( pier_code = 'C' base_fee = '40.00' rate_per_m = '5.00' power_fee = '8.00' ) ). + + lt_berth = VALUE #( ( berth_id = 'H1' boat_name = 'SEAGULL' length_m = '7.50' pier_code = 'A' shore_power = 'N' stay_days = 2 ) + ( berth_id = 'H2' boat_name = 'MARLIN' length_m = '8.00' pier_code = 'A' shore_power = 'Y' stay_days = 3 ) + ( berth_id = 'H3' boat_name = 'OTTER' length_m = '15.00' pier_code = 'B' shore_power = 'N' stay_days = 1 ) + ( berth_id = 'H4' boat_name = 'PELICAN' length_m = '15.01' pier_code = 'B' shore_power = 'Y' stay_days = 2 ) + ( berth_id = 'H5' boat_name = 'DOLPHIN' length_m = '9.99' pier_code = 'B' shore_power = 'N' stay_days = 3 ) + ( berth_id = 'H6' boat_name = 'SKUA' length_m = '9.99' pier_code = 'B' shore_power = 'Y' stay_days = 3 ) + ( berth_id = 'H7' boat_name = 'TERN' length_m = '12.34' pier_code = 'C' shore_power = 'N' stay_days = 4 ) + ( berth_id = 'H8' boat_name = 'GULL' length_m = '6.00' pier_code = 'Z' shore_power = 'N' stay_days = 1 ) + ( berth_id = 'H9' boat_name = 'HERON' length_m = '20.00' pier_code = 'C' shore_power = 'Y' stay_days = 1 ) ). + + go_env->insert_test_data( lt_pier ). + go_env->insert_test_data( lt_berth ). + ENDMETHOD. + + METHOD small_class. + SELECT SINGLE size_class FROM {{p}}i_berth_fee + WHERE berth_id = 'H1' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'SMALL' act = condense( val = lv_class ) ). + ENDMETHOD. + + METHOD medium_class. + SELECT SINGLE size_class FROM {{p}}i_berth_fee + WHERE berth_id = 'H2' INTO @DATA(lv_lower). + SELECT SINGLE size_class FROM {{p}}i_berth_fee + WHERE berth_id = 'H3' INTO @DATA(lv_upper). + cl_abap_unit_assert=>assert_equals( exp = 'MEDIUM' act = condense( val = lv_lower ) ). + cl_abap_unit_assert=>assert_equals( exp = 'MEDIUM' act = condense( val = lv_upper ) ). + ENDMETHOD. + + METHOD large_class. + SELECT SINGLE size_class FROM {{p}}i_berth_fee + WHERE berth_id = 'H4' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'LARGE' act = condense( val = lv_class ) ). + ENDMETHOD. + + METHOD length_in_centimetres. + SELECT SINGLE length_cm FROM {{p}}i_berth_fee + WHERE berth_id = 'H1' INTO @DATA(lv_small). + SELECT SINGLE length_cm FROM {{p}}i_berth_fee + WHERE berth_id = 'H5' INTO @DATA(lv_medium). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 750 ) + act = CONV decfloat34( lv_small ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 999 ) + act = CONV decfloat34( lv_medium ) ). + ENDMETHOD. + + METHOD fee_without_power. + SELECT SINGLE total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'H1' INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '85.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD power_surcharge. + SELECT SINGLE total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'H2' INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '147.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD rounds_after_days. + SELECT SINGLE total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'H5' INTO @DATA(lv_plain). + SELECT SINGLE total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'H6' INTO @DATA(lv_power). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '76.45' ) + act = CONV decfloat34( lv_plain ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '82.45' ) + act = CONV decfloat34( lv_power ) ). + ENDMETHOD. + + METHOD large_boat_fee. + SELECT SINGLE total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'H9' INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '148.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD unknown_pier_ignored. + SELECT COUNT(*) FROM {{p}}i_berth_fee INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 8 ) + act = CONV decfloat34( lv_rows ) ). + ENDMETHOD. + + METHOD boat_name_shown. + SELECT SINGLE boat_name FROM {{p}}i_berth_fee + WHERE berth_id = 'H3' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'OTTER' act = condense( val = lv_name ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1100/mutation.json b/tasks_gen/train/G1100/mutation.json new file mode 100644 index 0000000..c4084e0 --- /dev/null +++ b/tasks_gen/train/G1100/mutation.json @@ -0,0 +1,62 @@ +{ + "task": "G1100", + "mutants": [ + { + "object": "{{P}}I_BERTH_FEE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "UNKNOWN_PIER_IGNORED" + ] + }, + { + "object": "{{P}}I_BERTH_FEE", + "mutant": "line 10: < -> >= (rel)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "LARGE_CLASS", + "MEDIUM_CLASS", + "SMALL_CLASS" + ] + }, + { + "object": "{{P}}I_BERTH_FEE", + "mutant": "line 11: 15 -> 16 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "LARGE_CLASS" + ] + }, + { + "object": "{{P}}I_BERTH_FEE", + "mutant": "line 15: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "FEE_WITHOUT_POWER", + "LARGE_BOAT_FEE", + "POWER_SURCHARGE", + "ROUNDS_AFTER_DAYS" + ] + }, + { + "object": "{{P}}I_BERTH_FEE", + "mutant": "line 16: = -> <> (eq)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "FEE_WITHOUT_POWER", + "LARGE_BOAT_FEE", + "POWER_SURCHARGE", + "ROUNDS_AFTER_DAYS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1100/reference/i_berth_fee.ddls.asddls b/tasks_gen/train/G1100/reference/i_berth_fee.ddls.asddls new file mode 100644 index 0000000..dfd6a93 --- /dev/null +++ b/tasks_gen/train/G1100/reference/i_berth_fee.ddls.asddls @@ -0,0 +1,22 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Berth fee' +define view entity {{P}}I_BERTH_FEE + as select from {{p}}berth as berth + inner join {{p}}pier as pier on pier.pier_code = berth.pier_code +{ + key berth.berth_id as berth_id, + berth.boat_name as boat_name, + case + when berth.length_m < 8 then 'SMALL' + when berth.length_m <= 15 then 'MEDIUM' + else 'LARGE' + end as size_class, + cast( berth.length_m * 100 as abap.int4 ) as length_cm, + cast( round( ( pier.base_fee + berth.length_m * pier.rate_per_m + + case when berth.shore_power = 'Y' + then pier.power_fee + else cast( 0 as abap.dec( 10, 2 ) ) + end ) + * berth.stay_days, 2 ) + as abap.dec( 15, 2 ) ) as total_fee +} diff --git a/tasks_gen/train/G1100/reference/t42_test.clas.abap b/tasks_gen/train/G1100/reference/t42_test.clas.abap new file mode 100644 index 0000000..5a9e211 --- /dev/null +++ b/tasks_gen/train/G1100/reference/t42_test.clas.abap @@ -0,0 +1,45 @@ +CLASS {{p}}t42_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS fee_of_one_berth FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BERTH_FEE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_pier TYPE STANDARD TABLE OF {{p}}pier WITH EMPTY KEY. + DATA lt_berth TYPE STANDARD TABLE OF {{p}}berth WITH EMPTY KEY. + + go_env->clear_doubles( ). + + lt_pier = VALUE #( ( pier_code = 'A' base_fee = '20.00' rate_per_m = '3.00' power_fee = '5.00' ) ). + lt_berth = VALUE #( ( berth_id = 'A' boat_name = 'TEST BOAT' length_m = '8.00' + pier_code = 'A' shore_power = 'N' stay_days = 2 ) ). + + go_env->insert_test_data( lt_pier ). + go_env->insert_test_data( lt_berth ). + ENDMETHOD. + + METHOD fee_of_one_berth. + SELECT SINGLE size_class, length_cm, total_fee FROM {{p}}i_berth_fee + WHERE berth_id = 'A' INTO @DATA(ls_result). + cl_abap_unit_assert=>assert_equals( exp = 'MEDIUM' + act = condense( val = ls_result-size_class ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 800 ) + act = CONV decfloat34( ls_result-length_cm ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '88.00' ) + act = CONV decfloat34( ls_result-total_fee ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1100/seed/berth.tabl.asabap b/tasks_gen/train/G1100/seed/berth.tabl.asabap new file mode 100644 index 0000000..e846b8e --- /dev/null +++ b/tasks_gen/train/G1100/seed/berth.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Berth booking' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}berth { + key client : abap.clnt not null; + key berth_id : abap.char(10) not null; + boat_name : abap.char(12); + length_m : abap.dec(6,2); + pier_code : abap.char(1); + shore_power : abap.char(1); + stay_days : abap.int4; +} diff --git a/tasks_gen/train/G1100/seed/pier.tabl.asabap b/tasks_gen/train/G1100/seed/pier.tabl.asabap new file mode 100644 index 0000000..bd33869 --- /dev/null +++ b/tasks_gen/train/G1100/seed/pier.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Pier rate' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}pier { + key client : abap.clnt not null; + key pier_code : abap.char(1) not null; + base_fee : abap.dec(10,2); + rate_per_m : abap.dec(10,2); + power_fee : abap.dec(10,2); +} diff --git a/tasks_gen/train/G1100/seed/seed_berth.clas.abap b/tasks_gen/train/G1100/seed/seed_berth.clas.abap new file mode 100644 index 0000000..c47fcf4 --- /dev/null +++ b/tasks_gen/train/G1100/seed/seed_berth.clas.abap @@ -0,0 +1,36 @@ +CLASS {{p}}seed_berth DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}seed_berth IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_pier TYPE STANDARD TABLE OF {{p}}pier WITH EMPTY KEY. + DATA lt_berth TYPE STANDARD TABLE OF {{p}}berth WITH EMPTY KEY. + + DELETE FROM {{p}}berth. + DELETE FROM {{p}}pier. + + lt_pier = VALUE #( ( pier_code = 'A' base_fee = '20.00' rate_per_m = '3.00' power_fee = '5.00' ) + ( pier_code = 'B' base_fee = '10.00' rate_per_m = '1.55' power_fee = '2.00' ) + ( pier_code = 'C' base_fee = '40.00' rate_per_m = '5.00' power_fee = '8.00' ) ). + + lt_berth = VALUE #( ( berth_id = 'B1' boat_name = 'SEAGULL' length_m = '7.50' pier_code = 'A' shore_power = 'N' stay_days = 2 ) + ( berth_id = 'B2' boat_name = 'MARLIN' length_m = '8.00' pier_code = 'A' shore_power = 'Y' stay_days = 3 ) + ( berth_id = 'B3' boat_name = 'OTTER' length_m = '15.00' pier_code = 'B' shore_power = 'N' stay_days = 1 ) + ( berth_id = 'B4' boat_name = 'PELICAN' length_m = '15.01' pier_code = 'B' shore_power = 'Y' stay_days = 2 ) + ( berth_id = 'B5' boat_name = 'DOLPHIN' length_m = '9.99' pier_code = 'B' shore_power = 'N' stay_days = 3 ) + ( berth_id = 'B6' boat_name = 'SKUA' length_m = '9.99' pier_code = 'B' shore_power = 'Y' stay_days = 3 ) + ( berth_id = 'B7' boat_name = 'TERN' length_m = '12.34' pier_code = 'C' shore_power = 'N' stay_days = 4 ) + ( berth_id = 'B8' boat_name = 'GULL' length_m = '6.00' pier_code = 'Z' shore_power = 'N' stay_days = 1 ) + ( berth_id = 'B9' boat_name = 'HERON' length_m = '20.00' pier_code = 'C' shore_power = 'Y' stay_days = 1 ) ). + + INSERT {{p}}pier FROM TABLE @lt_pier. + INSERT {{p}}berth FROM TABLE @lt_berth. + + out->write( |Seed data inserted: { lines( lt_berth ) } berth bookings| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1100/spec.md b/tasks_gen/train/G1100/spec.md new file mode 100644 index 0000000..b981757 --- /dev/null +++ b/tasks_gen/train/G1100/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A marina rents berths to boat owners. The fee of a berth depends on the pier +and on the length of the boat. A report and an OData service will read the +fees. Therefore the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}BERTH (berth booking), package $TMP. Key: BERTH_ID (CHAR 10). + Fields: BOAT_NAME (CHAR 12), LENGTH_M (DEC 6,2, the length of the boat in + metres), PIER_CODE (CHAR 1), SHORE_POWER (CHAR 1, 'Y' = the boat uses shore + power, 'N' = it does not), STAY_DAYS (INT4, the number of days of the + booking). +- Table {{P}}PIER (pier rate), package $TMP. Key: PIER_CODE (CHAR 1). + Fields: BASE_FEE (DEC 10,2, for one day), RATE_PER_M (DEC 10,2, for one metre + and one day), POWER_FEE (DEC 10,2, for one day of shore power). + +# 4. Contract +- Create the CDS view entity {{P}}I_BERTH_FEE in package $TMP. +- Elements, with these names: BERTH_ID (key), BOAT_NAME, SIZE_CLASS, LENGTH_CM, + TOTAL_FEE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each berth booking that has a pier in {{P}}PIER. Do not show + a booking whose PIER_CODE is not in {{P}}PIER. +2. SIZE_CLASS is 'SMALL' if LENGTH_M is less than 8.00. SIZE_CLASS is 'MEDIUM' + if LENGTH_M is 8.00 or more and 15.00 or less. SIZE_CLASS is 'LARGE' if + LENGTH_M is more than 15.00. +3. LENGTH_CM is the length of the boat in whole centimetres. +4. The fee for one day is the base fee of the pier plus LENGTH_M multiplied by + the rate per metre of the pier. If SHORE_POWER is 'Y', add the power fee of + the pier. Do not round this value yet. +5. TOTAL_FEE is the fee for one day multiplied by STAY_DAYS, rounded to 2 + decimals. +6. BOAT_NAME is the name of the boat. + +# 6. Constraints +- Release target: 7.58. +- Use Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}BERTH and {{P}}PIER. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1100/task.json b/tasks_gen/train/G1100/task.json new file mode 100644 index 0000000..60dc9c2 --- /dev/null +++ b/tasks_gen/train/G1100/task.json @@ -0,0 +1,73 @@ +{ + "id": "G1100", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}BERTH", + "file": "seed/berth.tabl.asabap", + "description": "Berth booking" + }, + { + "type": "TABL", + "name": "{{P}}PIER", + "file": "seed/pier.tabl.asabap", + "description": "Pier rate" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_BERTH", + "file": "seed/seed_berth.clas.abap", + "description": "Seed data for both tables", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_BERTH_FEE", + "fields": [ + "BERTH_ID", + "BOAT_NAME", + "SIZE_CLASS", + "LENGTH_CM", + "TOTAL_FEE" + ] + } + ], + "out_of_scope": [ + "{{P}}BERTH", + "{{P}}PIER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T42_HIDDEN", + "file": "hidden/t42_hidden.clas.abap", + "description": "T42 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_BERTH_FEE", + "file": "reference/i_berth_fee.ddls.asddls", + "description": "Berth fee" + }, + { + "type": "CLAS", + "name": "{{P}}T42_TEST", + "file": "reference/t42_test.clas.abap", + "description": "T42 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1101/generation.json b/tasks_gen/train/G1101/generation.json new file mode 100644 index 0000000..80f5ed7 --- /dev/null +++ b/tasks_gen/train/G1101/generation.json @@ -0,0 +1,36 @@ +{ + "id": "G1101", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/dish.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/dish.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "seed/pos.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/pos.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1101/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1101/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..e34e35e --- /dev/null +++ b/tasks_gen/train/G1101/hidden/t41_hidden.clas.abap @@ -0,0 +1,212 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS setup. + METHODS teardown. + METHODS insert_dishes. + METHODS insert_positions + IMPORTING it_positions TYPE STANDARD TABLE OF {{p}}pos. + METHODS call + IMPORTING iv_order_id TYPE string + EXPORTING ev_total TYPE decfloat34 + ev_portions TYPE i + ev_dishes TYPE i + ev_subrc TYPE sysubrc. + + METHODS total_and_portions FOR TESTING. + METHODS distinct_dishes FOR TESTING. + METHODS ignores_other_orders FOR TESTING. + METHODS empty_order FOR TESTING. + METHODS zero_quantity FOR TESTING. + METHODS unknown_dish FOR TESTING. + METHODS quantity_before_dish FOR TESTING. + METHODS discount_from_fifty FOR TESTING. + METHODS no_discount_below_fifty FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + + METHOD setup. + DELETE FROM {{p}}pos WHERE client = @sy-mandt. + DELETE FROM {{p}}dish WHERE client = @sy-mandt. + insert_dishes( ). + ENDMETHOD. + + METHOD teardown. + DELETE FROM {{p}}pos WHERE client = @sy-mandt. + DELETE FROM {{p}}dish WHERE client = @sy-mandt. + ENDMETHOD. + + METHOD insert_dishes. + DATA lt_dishes TYPE STANDARD TABLE OF {{p}}dish. + lt_dishes = VALUE #( + ( client = sy-mandt dish_id = 'D001' dish_name = 'Tomato soup' price = '4.50' ) + ( client = sy-mandt dish_id = 'D002' dish_name = 'Roast beef' price = '18.00' ) + ( client = sy-mandt dish_id = 'D003' dish_name = 'Apple pie' price = '5.25' ) + ( client = sy-mandt dish_id = 'D004' dish_name = 'Sparkling water' price = '2.00' ) + ( client = sy-mandt dish_id = 'D005' dish_name = 'Bread basket' price = '2.05' ) + ). + INSERT {{p}}dish FROM TABLE @lt_dishes. + ENDMETHOD. + + METHOD insert_positions. + INSERT {{p}}pos FROM TABLE @it_positions. + ENDMETHOD. + + METHOD call. + CLEAR: ev_total, ev_portions, ev_dishes. + + CALL FUNCTION '{{P}}BANQUET_FEE' + EXPORTING + iv_order_id = iv_order_id + IMPORTING + ev_total = ev_total + ev_portions = ev_portions + ev_dishes = ev_dishes + EXCEPTIONS + no_positions = 1 + unknown_dish = 2 + invalid_quantity = 3 + OTHERS = 4. + + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD total_and_portions. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 2 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D002' qty = 1 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_total = DATA(lv_total) + ev_portions = DATA(lv_portions) + ev_dishes = DATA(lv_dishes) + ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '27.00' ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_portions ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_dishes ). + ENDMETHOD. + + METHOD distinct_dishes. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 2 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D001' qty = 3 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 3 dish_id = 'D003' qty = 1 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_total = DATA(lv_total) + ev_portions = DATA(lv_portions) + ev_dishes = DATA(lv_dishes) + ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '27.75' ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_portions ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_dishes ). + ENDMETHOD. + + METHOD ignores_other_orders. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41B' pos_no = 1 dish_id = 'D002' qty = 10 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 2 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_total = DATA(lv_total) + ev_portions = DATA(lv_portions) + ev_dishes = DATA(lv_dishes) + ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '9.00' ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_portions ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_dishes ). + ENDMETHOD. + + METHOD empty_order. + call( EXPORTING iv_order_id = 'T41Z' + IMPORTING ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD zero_quantity. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 0 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ). + ENDMETHOD. + + METHOD unknown_dish. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'X999' qty = 2 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD quantity_before_dish. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'X999' qty = 0 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ). + ENDMETHOD. + + METHOD discount_from_fifty. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D004' qty = 49 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D005' qty = 1 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_total = DATA(lv_total) + ev_portions = DATA(lv_portions) + ev_dishes = DATA(lv_dishes) + ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = lv_portions ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_dishes ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '95.05' ) act = lv_total ). + ENDMETHOD. + + METHOD no_discount_below_fifty. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D004' qty = 49 ) ). + insert_positions( lt_positions ). + + call( EXPORTING iv_order_id = 'T41A' + IMPORTING ev_total = DATA(lv_total) + ev_portions = DATA(lv_portions) + ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 49 act = lv_portions ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '98.00' ) act = lv_total ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1101/mutation.json b/tasks_gen/train/G1101/mutation.json new file mode 100644 index 0000000..56afb35 --- /dev/null +++ b/tasks_gen/train/G1101/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "T41", + "mutants": [ + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 30: + -> - (arith)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "TOTAL_AND_PORTIONS", + "DISTINCT_DISHES", + "IGNORES_OTHER_ORDERS", + "DISCOUNT_FROM_FIFTY", + "NO_DISCOUNT_BELOW_FIFTY" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 22: IS INITIAL -> IS NOT INITIAL (bool)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "EMPTY_ORDER" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 27: <= -> < (rel)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "ZERO_QUANTITY", + "QUANTITY_BEFORE_DISH" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 36: <> -> = (rel)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "UNKNOWN_DISH" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 41: + -> - (arith)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "TOTAL_AND_PORTIONS" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 47: >= -> > (rel)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "DISCOUNT_FROM_FIFTY" + ] + }, + { + "object": "{{P}}BANQUET_FEE", + "mutant": "line 48: 95 -> 90 (const)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "DISCOUNT_FROM_FIFTY" + ] + } + ], + "valid": 7, + "killed": 7, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1101/reference/banquet_fee.func.abap b/tasks_gen/train/G1101/reference/banquet_fee.func.abap new file mode 100644 index 0000000..47b2b50 --- /dev/null +++ b/tasks_gen/train/G1101/reference/banquet_fee.func.abap @@ -0,0 +1,57 @@ +FUNCTION {{p}}banquet_fee + IMPORTING + iv_order_id TYPE string + EXPORTING + ev_total TYPE decfloat34 + ev_portions TYPE i + ev_dishes TYPE i + EXCEPTIONS + no_positions + unknown_dish + invalid_quantity. + + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + DATA lt_dishes TYPE HASHED TABLE OF {{p}}dish WITH UNIQUE KEY dish_id. + DATA lt_seen TYPE HASHED TABLE OF {{p}}pos WITH UNIQUE KEY dish_id. + DATA ls_position TYPE {{p}}pos. + DATA ls_dish TYPE {{p}}dish. + + CLEAR: ev_total, ev_portions, ev_dishes. + + SELECT order_id, pos_no, dish_id, qty + FROM {{p}}pos + WHERE order_id = @iv_order_id + INTO TABLE @lt_positions. + + IF lt_positions IS INITIAL. + RAISE no_positions. + ENDIF. + + LOOP AT lt_positions INTO ls_position. + IF ls_position-qty <= 0. + RAISE invalid_quantity. + ENDIF. + ENDLOOP. + + SELECT dish_id, dish_name, price + FROM {{p}}dish + INTO TABLE @lt_dishes. + + LOOP AT lt_positions INTO ls_position. + READ TABLE lt_dishes INTO ls_dish WITH TABLE KEY dish_id = ls_position-dish_id. + IF sy-subrc <> 0. + RAISE unknown_dish. + ENDIF. + + ev_total = ev_total + ls_dish-price * ls_position-qty. + ev_portions = ev_portions + ls_position-qty. + INSERT ls_position INTO TABLE lt_seen. + ENDLOOP. + + ev_dishes = lines( lt_seen ). + + IF ev_portions >= 50. + ev_total = round( val = ev_total * 95 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1101/reference/t41_test.clas.abap b/tasks_gen/train/G1101/reference/t41_test.clas.abap new file mode 100644 index 0000000..242f518 --- /dev/null +++ b/tasks_gen/train/G1101/reference/t41_test.clas.abap @@ -0,0 +1,120 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS setup. + METHODS teardown. + METHODS insert_dishes. + METHODS insert_positions + IMPORTING it_positions TYPE STANDARD TABLE OF {{p}}pos. + METHODS fee_for + IMPORTING iv_order_id TYPE string + RETURNING VALUE(rv_total) TYPE decfloat34. + + METHODS total_of_order FOR TESTING. + METHODS distinct_dishes FOR TESTING. + METHODS discount_from_fifty FOR TESTING. + METHODS empty_order FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + + METHOD setup. + DELETE FROM {{p}}pos WHERE client = @sy-mandt. + DELETE FROM {{p}}dish WHERE client = @sy-mandt. + insert_dishes( ). + ENDMETHOD. + + METHOD teardown. + DELETE FROM {{p}}pos WHERE client = @sy-mandt. + DELETE FROM {{p}}dish WHERE client = @sy-mandt. + ENDMETHOD. + + METHOD insert_dishes. + DATA lt_dishes TYPE STANDARD TABLE OF {{p}}dish. + lt_dishes = VALUE #( + ( client = sy-mandt dish_id = 'D001' dish_name = 'Tomato soup' price = '4.50' ) + ( client = sy-mandt dish_id = 'D002' dish_name = 'Roast beef' price = '18.00' ) + ( client = sy-mandt dish_id = 'D003' dish_name = 'Apple pie' price = '5.25' ) + ( client = sy-mandt dish_id = 'D004' dish_name = 'Sparkling water' price = '2.00' ) + ( client = sy-mandt dish_id = 'D005' dish_name = 'Bread basket' price = '2.05' ) + ). + INSERT {{p}}dish FROM TABLE @lt_dishes. + ENDMETHOD. + + METHOD insert_positions. + INSERT {{p}}pos FROM TABLE @it_positions. + ENDMETHOD. + + METHOD fee_for. + CALL FUNCTION '{{P}}BANQUET_FEE' + EXPORTING + iv_order_id = iv_order_id + IMPORTING + ev_total = rv_total + EXCEPTIONS + OTHERS = 1. + + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD total_of_order. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 2 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D002' qty = 1 ) ). + insert_positions( lt_positions ). + + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '27.00' ) + act = fee_for( 'T41A' ) ). + ENDMETHOD. + + METHOD distinct_dishes. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D001' qty = 2 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D001' qty = 3 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 3 dish_id = 'D003' qty = 1 ) ). + insert_positions( lt_positions ). + + DATA lv_dishes TYPE i. + CALL FUNCTION '{{P}}BANQUET_FEE' + EXPORTING + iv_order_id = 'T41A' + IMPORTING + ev_dishes = lv_dishes + EXCEPTIONS + OTHERS = 1. + + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_dishes ). + ENDMETHOD. + + METHOD discount_from_fifty. + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'T41A' pos_no = 1 dish_id = 'D004' qty = 49 ) + ( client = sy-mandt order_id = 'T41A' pos_no = 2 dish_id = 'D005' qty = 1 ) ). + insert_positions( lt_positions ). + + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '95.05' ) + act = fee_for( 'T41A' ) ). + ENDMETHOD. + + METHOD empty_order. + DATA lv_total TYPE decfloat34. + CALL FUNCTION '{{P}}BANQUET_FEE' + EXPORTING + iv_order_id = 'T41Z' + IMPORTING + ev_total = lv_total + EXCEPTIONS + no_positions = 1 + OTHERS = 2. + + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1101/seed/dish.tabl.abap b/tasks_gen/train/G1101/seed/dish.tabl.abap new file mode 100644 index 0000000..2aac74c --- /dev/null +++ b/tasks_gen/train/G1101/seed/dish.tabl.abap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Dish master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}dish { + key client : abap.clnt not null; + key dish_id : abap.char(10) not null; + dish_name : abap.char(40); + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1101/seed/pos.tabl.abap b/tasks_gen/train/G1101/seed/pos.tabl.abap new file mode 100644 index 0000000..1867231 --- /dev/null +++ b/tasks_gen/train/G1101/seed/pos.tabl.abap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Banquet positions' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}pos { + key client : abap.clnt not null; + key order_id : abap.char(10) not null; + key pos_no : abap.int4 not null; + dish_id : abap.char(10); + qty : abap.int4; +} diff --git a/tasks_gen/train/G1101/seed/seed_banquet.clas.abap b/tasks_gen/train/G1101/seed/seed_banquet.clas.abap new file mode 100644 index 0000000..df9e4b6 --- /dev/null +++ b/tasks_gen/train/G1101/seed/seed_banquet.clas.abap @@ -0,0 +1,36 @@ +CLASS {{p}}seed_banquet DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_banquet IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}pos WHERE client = @sy-mandt. + DELETE FROM {{p}}dish WHERE client = @sy-mandt. + + DATA lt_dishes TYPE STANDARD TABLE OF {{p}}dish. + lt_dishes = VALUE #( + ( client = sy-mandt dish_id = 'D001' dish_name = 'Tomato soup' price = '4.50' ) + ( client = sy-mandt dish_id = 'D002' dish_name = 'Roast beef' price = '18.00' ) + ( client = sy-mandt dish_id = 'D003' dish_name = 'Apple pie' price = '5.25' ) + ( client = sy-mandt dish_id = 'D004' dish_name = 'Sparkling water' price = '2.00' ) + ( client = sy-mandt dish_id = 'D005' dish_name = 'Bread basket' price = '2.05' ) + ). + INSERT {{p}}dish FROM TABLE @lt_dishes. + + DATA lt_positions TYPE STANDARD TABLE OF {{p}}pos. + lt_positions = VALUE #( + ( client = sy-mandt order_id = 'ORD1' pos_no = 1 dish_id = 'D001' qty = 12 ) + ( client = sy-mandt order_id = 'ORD1' pos_no = 2 dish_id = 'D002' qty = 8 ) + ( client = sy-mandt order_id = 'ORD1' pos_no = 3 dish_id = 'D004' qty = 30 ) + ( client = sy-mandt order_id = 'ORD2' pos_no = 1 dish_id = 'D003' qty = 4 ) + ( client = sy-mandt order_id = 'ORD3' pos_no = 1 dish_id = 'D001' qty = 0 ) + ( client = sy-mandt order_id = 'ORD4' pos_no = 1 dish_id = 'X999' qty = 2 ) + ). + INSERT {{p}}pos FROM TABLE @lt_positions. + + out->write( |Dishes: { lines( lt_dishes ) }| ). + out->write( |Positions: { lines( lt_positions ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1101/spec.md b/tasks_gen/train/G1101/spec.md new file mode 100644 index 0000000..7cc9684 --- /dev/null +++ b/tasks_gen/train/G1101/spec.md @@ -0,0 +1,61 @@ +# 1. Goal +A catering company prices banquet orders. The order desk calls one function +module for each banquet order. The function module reads the ordered positions +of the order and the dish master, and it returns the total price, the number of +portions and the number of different dishes. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_BANQUET exists in package $TMP. +- Table {{P}}DISH is the dish master. Its fields are DISH_ID (character 10), + DISH_NAME (character 40) and PRICE (amount with 2 decimals). +- Table {{P}}POS holds the positions of the banquet orders. Its fields are + ORDER_ID (character 10), POS_NO (integer), DISH_ID (character 10) and QTY + (integer). The key of the table is ORDER_ID and POS_NO. The same dish can + occur in more than one position of the same order. +- The order desk passes only the order number to the function module. The + function module reads the two tables. + +# 4. Contract +Create the function module {{P}}BANQUET_FEE in function group {{P}}FG_BANQUET. + +The interface of the function module is: +- IMPORTING + - IV_ORDER_ID: string +- EXPORTING + - EV_TOTAL: decfloat34 + - EV_PORTIONS: i + - EV_DISHES: i +- EXCEPTIONS + - NO_POSITIONS + - UNKNOWN_DISH + - INVALID_QUANTITY + +# 5. Business rules +1. The positions of the order are the rows of {{P}}POS with the given order + number. +2. If the order has no position, raise NO_POSITIONS. +3. If a position has a quantity of 0 or less, raise INVALID_QUANTITY. +4. If a position refers to a dish that is not in {{P}}DISH, raise UNKNOWN_DISH. +5. If more than one of the rules 2 to 4 applies, the function module raises the + exception of the lowest rule number. +6. EV_TOTAL is the sum of the quantity multiplied by the price of the dish over + all positions of the order. +7. EV_PORTIONS is the sum of the quantities of all positions of the order. +8. EV_DISHES is the number of different dishes in the order. +9. If EV_PORTIONS is 50 or more, the caterer gives a discount of 5 % on + EV_TOTAL. Round the discounted total half up to 2 decimals. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816). +- Coding standards: Clean ABAP. A method has fewer than 40 statements. The + function module must not cause an ATC finding of priority 1 or 2. +- Out of scope: do not change {{P}}DISH, {{P}}POS, {{P}}FG_BANQUET and + {{P}}SEED_BANQUET. + +# 7. Acceptance +- The function module {{P}}BANQUET_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1101/task.json b/tasks_gen/train/G1101/task.json new file mode 100644 index 0000000..d5b7b08 --- /dev/null +++ b/tasks_gen/train/G1101/task.json @@ -0,0 +1,97 @@ +{ + "id": "G1101", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_BANQUET", + "description": "Banquet pricing" + }, + { + "type": "TABL", + "name": "{{P}}DISH", + "file": "seed/dish.tabl.abap", + "description": "Dish master" + }, + { + "type": "TABL", + "name": "{{P}}POS", + "file": "seed/pos.tabl.abap", + "description": "Banquet positions" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_BANQUET", + "file": "seed/seed_banquet.clas.abap", + "description": "Seed data for the dish master and the banquet positions", + "run": true + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}BANQUET_FEE", + "functionGroup": "{{P}}FG_BANQUET", + "params": [ + { + "name": "iv_order_id", + "type": "string" + }, + { + "name": "ev_total", + "type": "decfloat34" + }, + { + "name": "ev_portions", + "type": "i" + }, + { + "name": "ev_dishes", + "type": "i" + } + ] + } + ], + "out_of_scope": [ + "{{P}}DISH", + "{{P}}POS", + "{{P}}FG_BANQUET", + "{{P}}SEED_BANQUET" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "Hidden tests for {{P}}BANQUET_FEE" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}BANQUET_FEE", + "functionGroup": "{{P}}FG_BANQUET", + "file": "reference/banquet_fee.func.abap", + "description": "Banquet fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "Own tests of the banquet fee module" + } + ], + "craft_checks": [ + "method_length", + "internal_table_key", + "no_linear_search_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1102/generation.json b/tasks_gen/train/G1102/generation.json new file mode 100644 index 0000000..29a0a69 --- /dev/null +++ b/tasks_gen/train/G1102/generation.json @@ -0,0 +1,46 @@ +{ + "id": "G1102", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tennis_old.prog.abap line 5: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECTION\"", + "Too close to task G1090 (similarity spec 0.61, rules 0.63, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G1090 (similarity spec 0.62, rules 0.63, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/farm_shop_old.prog.abap line 114: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_goods\"", + "reference/farm_shop_old.prog.abap line 122: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_sale\"", + "reference/farm_shop_old.prog.abap line 156: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/farm_shop_old.prog.abap line 158: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1102/hidden/shop_hidden.clas.abap b/tasks_gen/train/G1102/hidden/shop_hidden.clas.abap new file mode 100644 index 0000000..3aea18a --- /dev/null +++ b/tasks_gen/train/G1102/hidden/shop_hidden.clas.abap @@ -0,0 +1,112 @@ +CLASS {{p}}shop_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_grp_range TYPE RANGE OF {{p}}goods-grp_code. + + METHODS run + IMPORTING iv_day TYPE d + it_grp TYPE tt_grp_range OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + + METHODS totals_per_group FOR TESTING. + METHODS sorted_by_turnover FOR TESTING. + METHODS share_of_total FOR TESTING. + METHODS cancelled_ignored FOR TESTING. + METHODS other_day_ignored FOR TESTING. + METHODS group_filter FOR TESTING. + METHODS empty_result FOR TESTING. +ENDCLASS. + + +CLASS {{p}}shop_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}farm_shop_old + WITH p_day = iv_day + WITH s_grp IN it_grp + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD totals_per_group. + DATA(lr_data) = run( iv_day = '20260314' ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `VEG` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GRP_CODE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SALES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `16.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TURNOVER` ) ). + ENDMETHOD. + + METHOD sorted_by_turnover. + DATA(lr_data) = run( iv_day = '20260314' ). + cl_abap_unit_assert=>assert_equals( exp = `VEG` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GRP_CODE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `DAI` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `GRP_CODE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `FRU` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `GRP_CODE` ) ). + ENDMETHOD. + + METHOD share_of_total. + DATA(lr_data) = run( iv_day = '20260314' ). + cl_abap_unit_assert=>assert_equals( exp = `39.0` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PCT_SHARE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `31.7` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `PCT_SHARE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `29.3` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `PCT_SHARE` ) ). + ENDMETHOD. + + METHOD cancelled_ignored. + DATA(lr_data) = run( iv_day = '20260314' ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SALES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `16.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TURNOVER` ) ). + ENDMETHOD. + + METHOD other_day_ignored. + DATA(lr_data) = run( iv_day = '20260313' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `BAK` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GRP_CODE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `6.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TURNOVER` ) ). + cl_abap_unit_assert=>assert_equals( exp = `FRU` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `GRP_CODE` ) ). + ENDMETHOD. + + METHOD group_filter. + DATA(lr_data) = run( iv_day = '20260314' + it_grp = VALUE #( ( sign = 'I' option = 'EQ' low = 'DAI' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `DAI` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GRP_CODE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SALES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `13.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TURNOVER` ) ). + cl_abap_unit_assert=>assert_equals( exp = `100.0` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PCT_SHARE` ) ). + ENDMETHOD. + + METHOD empty_result. + DATA(lr_data) = run( iv_day = '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( lr_data ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1102/reference/farm_shop_old.prog.abap b/tasks_gen/train/G1102/reference/farm_shop_old.prog.abap new file mode 100644 index 0000000..164aaa0 --- /dev/null +++ b/tasks_gen/train/G1102/reference/farm_shop_old.prog.abap @@ -0,0 +1,172 @@ +REPORT {{p}}farm_shop_old. + +DATA gv_grp_code TYPE {{p}}goods-grp_code. + +PARAMETERS p_day TYPE d OBLIGATORY. +SELECT-OPTIONS s_grp FOR gv_grp_code. + +CLASS lcl_shop_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + grp_code TYPE {{p}}goods-grp_code, + sales TYPE i, + turnover TYPE {{p}}goods-unit_price, + pct_share TYPE p LENGTH 5 DECIMALS 1, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_grp_range TYPE RANGE OF {{p}}goods-grp_code. + + METHODS get_totals + IMPORTING iv_day TYPE d + it_grp TYPE tt_grp_range + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. + + PRIVATE SECTION. + METHODS set_shares + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_shop_report IMPLEMENTATION. + METHOD get_totals. + TYPES: + BEGIN OF ty_row, + grp_code TYPE {{p}}goods-grp_code, + qty TYPE {{p}}gdsale-qty, + unit_price TYPE {{p}}goods-unit_price, + END OF ty_row. + DATA lt_rows TYPE STANDARD TABLE OF ty_row WITH EMPTY KEY. + + SELECT g~grp_code, s~qty, g~unit_price + FROM {{p}}gdsale AS s + INNER JOIN {{p}}goods AS g ON g~goods_id = s~goods_id + WHERE s~sale_state = 'P' + AND s~sale_day = @iv_day + AND g~grp_code IN @it_grp + INTO TABLE @lt_rows. + + DATA lt_totals TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY grp_code. + + LOOP AT lt_rows INTO DATA(ls_row). + ASSIGN lt_totals[ grp_code = ls_row-grp_code ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( grp_code = ls_row-grp_code ) INTO TABLE lt_totals ASSIGNING . + ENDIF. + -sales = -sales + 1. + -turnover = -turnover + ls_row-qty * ls_row-unit_price. + ENDLOOP. + + rt_lines = CORRESPONDING #( lt_totals ). + set_shares( CHANGING ct_lines = rt_lines ). + SORT rt_lines BY turnover DESCENDING. + ENDMETHOD. + + METHOD set_shares. + DATA lv_total TYPE {{p}}goods-unit_price. + LOOP AT ct_lines INTO DATA(ls_line). + lv_total = lv_total + ls_line-turnover. + ENDLOOP. + IF lv_total = 0. + RETURN. + ENDIF. + LOOP AT ct_lines ASSIGNING FIELD-SYMBOL(). + -pct_share = -turnover * 100 / lv_total. + ENDLOOP. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_report) = NEW lcl_shop_report( ). + DATA(lt_lines) = lo_report->get_totals( iv_day = p_day it_grp = s_grp[] ). + lo_report->display( CHANGING ct_lines = lt_lines ). + +CLASS ltc_shop_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_share TYPE p LENGTH 5 DECIMALS 1. + + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_group FOR TESTING. + METHODS sorted_by_turnover FOR TESTING. + METHODS share_of_total FOR TESTING. + METHODS cancelled_ignored FOR TESTING. +ENDCLASS. + +CLASS ltc_shop_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}GOODS' ) ( '{{P}}GDSALE' ) ) ). + + DATA lt_goods TYPE STANDARD TABLE OF {{p}}goods WITH EMPTY KEY. + lt_goods = VALUE #( ( goods_id = 'G001' grp_code = 'VEG' unit_price = '2.50' ) + ( goods_id = 'G002' grp_code = 'VEG' unit_price = '3.00' ) + ( goods_id = 'G003' grp_code = 'FRU' unit_price = '4.00' ) + ( goods_id = 'G004' grp_code = 'DAI' unit_price = '5.00' ) + ( goods_id = 'G005' grp_code = 'DAI' unit_price = '1.50' ) ). + go_env->insert_test_data( lt_goods ). + + DATA lt_sale TYPE STANDARD TABLE OF {{p}}gdsale WITH EMPTY KEY. + lt_sale = VALUE #( ( sale_id = 1 goods_id = 'G001' sale_day = '20260314' qty = 4 sale_state = 'P' ) + ( sale_id = 2 goods_id = 'G002' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 3 goods_id = 'G003' sale_day = '20260314' qty = 3 sale_state = 'P' ) + ( sale_id = 4 goods_id = 'G004' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 5 goods_id = 'G005' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 6 goods_id = 'G001' sale_day = '20260314' qty = 5 sale_state = 'C' ) ). + go_env->insert_test_data( lt_sale ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_group. + DATA(lt_lines) = NEW lcl_shop_report( )->get_totals( + iv_day = '20260314' it_grp = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'VEG' act = lt_lines[ 1 ]-grp_code ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sales ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}goods-unit_price( 16 ) + act = lt_lines[ 1 ]-turnover ). + ENDMETHOD. + + METHOD sorted_by_turnover. + DATA(lt_lines) = NEW lcl_shop_report( )->get_totals( + iv_day = '20260314' it_grp = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'VEG' act = lt_lines[ 1 ]-grp_code ). + cl_abap_unit_assert=>assert_equals( exp = 'DAI' act = lt_lines[ 2 ]-grp_code ). + cl_abap_unit_assert=>assert_equals( exp = 'FRU' act = lt_lines[ 3 ]-grp_code ). + ENDMETHOD. + + METHOD share_of_total. + DATA(lt_lines) = NEW lcl_shop_report( )->get_totals( + iv_day = '20260314' it_grp = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_share( '39.0' ) + act = lt_lines[ 1 ]-pct_share ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_share( '31.7' ) + act = lt_lines[ 2 ]-pct_share ). + ENDMETHOD. + + METHOD cancelled_ignored. + DATA(lt_lines) = NEW lcl_shop_report( )->get_totals( + iv_day = '20260314' it_grp = VALUE #( ( sign = 'I' option = 'EQ' low = 'VEG' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sales ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}goods-unit_price( 16 ) + act = lt_lines[ 1 ]-turnover ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1102/seed/farm_shop_old.prog.abap b/tasks_gen/train/G1102/seed/farm_shop_old.prog.abap new file mode 100644 index 0000000..28eccec --- /dev/null +++ b/tasks_gen/train/G1102/seed/farm_shop_old.prog.abap @@ -0,0 +1,83 @@ +REPORT {{p}}farm_shop_old. + +DATA: BEGIN OF gt_sale OCCURS 0, + sale_id TYPE {{p}}gdsale-sale_id, + goods_id TYPE {{p}}gdsale-goods_id, + sale_day TYPE {{p}}gdsale-sale_day, + qty TYPE {{p}}gdsale-qty, + sale_state TYPE {{p}}gdsale-sale_state, + grp_code TYPE {{p}}goods-grp_code, + unit_price TYPE {{p}}goods-unit_price, + END OF gt_sale. + +DATA: BEGIN OF gt_out OCCURS 0, + grp_code TYPE {{p}}goods-grp_code, + sales TYPE i, + turnover TYPE {{p}}goods-unit_price, + pct_share TYPE p LENGTH 5 DECIMALS 1, + END OF gt_out. + +DATA: gv_total TYPE {{p}}goods-unit_price, + go_alv TYPE REF TO cl_salv_table. + +PARAMETERS p_day TYPE d OBLIGATORY. +SELECT-OPTIONS s_grp FOR {{p}}goods-grp_code. + +START-OF-SELECTION. + PERFORM read_data. + PERFORM calc_totals. + PERFORM show_list. + +FORM read_data. + SELECT * FROM {{p}}gdsale INTO CORRESPONDING FIELDS OF TABLE gt_sale + WHERE sale_state = 'P' + AND sale_day = p_day. + LOOP AT gt_sale. + SELECT SINGLE grp_code unit_price FROM {{p}}goods + INTO (gt_sale-grp_code, gt_sale-unit_price) + WHERE goods_id = gt_sale-goods_id. + ENDLOOP. + DELETE gt_sale WHERE grp_code IS INITIAL. + IF s_grp IS NOT INITIAL. + DELETE gt_sale WHERE grp_code NOT IN s_grp. + ENDIF. + SORT gt_sale BY grp_code. +ENDFORM. + +FORM calc_totals. + LOOP AT gt_sale. + AT NEW grp_code. + CLEAR gt_out. + gt_out-grp_code = gt_sale-grp_code. + ENDAT. + gt_out-sales = gt_out-sales + 1. + gt_out-turnover = gt_out-turnover + gt_sale-qty * gt_sale-unit_price. + AT END OF grp_code. + APPEND gt_out. + ENDAT. + ENDLOOP. + + CLEAR gv_total. + LOOP AT gt_out. + gv_total = gv_total + gt_out-turnover. + ENDLOOP. + + IF gv_total <> 0. + LOOP AT gt_out. + gt_out-pct_share = gt_out-turnover * 100 / gv_total. + MODIFY gt_out. + ENDLOOP. + ENDIF. + + SORT gt_out BY turnover DESCENDING. +ENDFORM. + +FORM show_list. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = go_alv + CHANGING t_table = gt_out[] ). + go_alv->display( ). + CATCH cx_salv_msg. + ENDTRY. +ENDFORM. diff --git a/tasks_gen/train/G1102/seed/gdsale.tabl.asabap b/tasks_gen/train/G1102/seed/gdsale.tabl.asabap new file mode 100644 index 0000000..f196191 --- /dev/null +++ b/tasks_gen/train/G1102/seed/gdsale.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Farm shop sale' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}gdsale { + key client : abap.clnt not null; + key sale_id : abap.int4 not null; + goods_id : abap.char(4); + sale_day : abap.dats; + qty : abap.int2; + sale_state : abap.char(1); +} diff --git a/tasks_gen/train/G1102/seed/goods.tabl.asabap b/tasks_gen/train/G1102/seed/goods.tabl.asabap new file mode 100644 index 0000000..70de144 --- /dev/null +++ b/tasks_gen/train/G1102/seed/goods.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Farm shop goods' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}goods { + key client : abap.clnt not null; + key goods_id : abap.char(4) not null; + grp_code : abap.char(4); + unit_price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1102/seed/shop_seed.clas.abap b/tasks_gen/train/G1102/seed/shop_seed.clas.abap new file mode 100644 index 0000000..86cd9f0 --- /dev/null +++ b/tasks_gen/train/G1102/seed/shop_seed.clas.abap @@ -0,0 +1,31 @@ +CLASS {{p}}shop_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}shop_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}gdsale. + DELETE FROM {{p}}goods. + + INSERT {{p}}goods FROM TABLE @( VALUE #( + ( goods_id = 'G001' grp_code = 'VEG' unit_price = '2.50' ) + ( goods_id = 'G002' grp_code = 'VEG' unit_price = '3.00' ) + ( goods_id = 'G003' grp_code = 'FRU' unit_price = '4.00' ) + ( goods_id = 'G004' grp_code = 'DAI' unit_price = '5.00' ) + ( goods_id = 'G005' grp_code = 'DAI' unit_price = '1.50' ) + ( goods_id = 'G006' grp_code = 'BAK' unit_price = '2.00' ) ) ). + + INSERT {{p}}gdsale FROM TABLE @( VALUE #( + ( sale_id = 1 goods_id = 'G001' sale_day = '20260314' qty = 4 sale_state = 'P' ) + ( sale_id = 2 goods_id = 'G002' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 3 goods_id = 'G003' sale_day = '20260314' qty = 3 sale_state = 'P' ) + ( sale_id = 4 goods_id = 'G004' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 5 goods_id = 'G005' sale_day = '20260314' qty = 2 sale_state = 'P' ) + ( sale_id = 6 goods_id = 'G001' sale_day = '20260314' qty = 5 sale_state = 'C' ) + ( sale_id = 7 goods_id = 'G003' sale_day = '20260313' qty = 1 sale_state = 'P' ) + ( sale_id = 8 goods_id = 'G006' sale_day = '20260313' qty = 3 sale_state = 'P' ) ) ). + + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1102/spec.md b/tasks_gen/train/G1102/spec.md new file mode 100644 index 0000000..8dedcba --- /dev/null +++ b/tasks_gen/train/G1102/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +The farm shop of a cooperative sells its own vegetables, fruit and dairy goods. +The report {{P}}FARM_SHOP_OLD shows the turnover of one day, grouped by product +group. The report is in production, but its code is outdated and hard to +maintain. Refactor the report in place: same name, same selection screen, same +output. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- {{P}}FARM_SHOP_OLD is an executable program in package $TMP. It is active. +- The report reads the tables {{P}}GOODS and {{P}}GDSALE. Both tables are in + package $TMP and contain data. + - {{P}}GOODS: GOODS_ID (CHAR 4, key), GRP_CODE (CHAR 4), UNIT_PRICE (DEC 15,2). + - {{P}}GDSALE: SALE_ID (INT4, key), GOODS_ID (CHAR 4), SALE_DAY (DATS), + QTY (INT2), SALE_STATE (CHAR 1). +- The report writes the result with CL_SALV_TABLE. +- The legacy implementation is the only specification of the rules. Read it. + +# 4. Contract +- The program {{P}}FARM_SHOP_OLD stays in package $TMP. Do not rename it. +- The selection screen stays the same: + - Parameter P_DAY, type DATS, mandatory. + - Select-option S_GRP for the product group. +- The ALV list keeps these columns, with these names: + GRP_CODE, SALES, TURNOVER, PCT_SHARE. + +# 5. Business rules +The behavior of the report stays the same. The legacy code is the only +specification. Keep every result exactly as it is today: the same rows, the +same values, the same order, the same filters. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816, ABAP Platform 2025). +- Coding standards: Clean ABAP. Methods below 40 statements. No obsolete + language elements. No database access inside a loop. No header-line tables. + Never mix old and new ABAP SQL syntax in one statement. +- Out of scope: do not change {{P}}GOODS and {{P}}GDSALE. Do not change the + selection screen. Do not change the names of the output columns. + +# 7. Acceptance +- {{P}}FARM_SHOP_OLD is active and has no syntax error. +- The behavior of the report is unchanged. +- The hidden tests pass. +- ATC reports no finding of priority 1 or 2. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1102/task.json b/tasks_gen/train/G1102/task.json new file mode 100644 index 0000000..6bddc1a --- /dev/null +++ b/tasks_gen/train/G1102/task.json @@ -0,0 +1,73 @@ +{ + "id": "G1102", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}GOODS", + "file": "seed/goods.tabl.asabap", + "description": "Farm shop goods" + }, + { + "type": "TABL", + "name": "{{P}}GDSALE", + "file": "seed/gdsale.tabl.asabap", + "description": "Farm shop sales" + }, + { + "type": "CLAS", + "name": "{{P}}SHOP_SEED", + "file": "seed/shop_seed.clas.abap", + "description": "Seed data for goods and sales", + "run": true + }, + { + "type": "PROG", + "name": "{{P}}FARM_SHOP_OLD", + "file": "seed/farm_shop_old.prog.abap", + "description": "Legacy report with old code" + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}FARM_SHOP_OLD", + "parameters": [ + "p_day", + "s_grp" + ] + } + ], + "out_of_scope": [ + "{{P}}GOODS", + "{{P}}GDSALE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SHOP_HIDDEN", + "file": "hidden/shop_hidden.clas.abap", + "description": "Hidden tests for the unchanged behavior" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}FARM_SHOP_OLD", + "file": "reference/farm_shop_old.prog.abap", + "description": "Refactored report with local test classes" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1103/faulty/m0_wash_fee.func.abap b/tasks_gen/train/G1103/faulty/m0_wash_fee.func.abap new file mode 100644 index 0000000..40da80f --- /dev/null +++ b/tasks_gen/train/G1103/faulty/m0_wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' OR iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price + '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax = abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_true. + lv_price = round( val = lv_price * 85 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/faulty/m1_wash_fee.func.abap b/tasks_gen/train/G1103/faulty/m1_wash_fee.func.abap new file mode 100644 index 0000000..f8e929a --- /dev/null +++ b/tasks_gen/train/G1103/faulty/m1_wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' AND iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price - '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax = abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_true. + lv_price = round( val = lv_price * 85 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/faulty/m2_wash_fee.func.abap b/tasks_gen/train/G1103/faulty/m2_wash_fee.func.abap new file mode 100644 index 0000000..a6af5fc --- /dev/null +++ b/tasks_gen/train/G1103/faulty/m2_wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' AND iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price + '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax <> abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_true. + lv_price = round( val = lv_price * 85 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/faulty/m3_wash_fee.func.abap b/tasks_gen/train/G1103/faulty/m3_wash_fee.func.abap new file mode 100644 index 0000000..d7e4697 --- /dev/null +++ b/tasks_gen/train/G1103/faulty/m3_wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' AND iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price + '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax = abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_false. + lv_price = round( val = lv_price * 85 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/faulty/m4_wash_fee.func.abap b/tasks_gen/train/G1103/faulty/m4_wash_fee.func.abap new file mode 100644 index 0000000..94f96ca --- /dev/null +++ b/tasks_gen/train/G1103/faulty/m4_wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' AND iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price + '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax = abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_true. + lv_price = round( val = lv_price * 85 / 100 dec = 3 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/generation.json b/tasks_gen/train/G1103/generation.json new file mode 100644 index 0000000..da4e908 --- /dev/null +++ b/tasks_gen/train/G1103/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1103", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1103/hidden/t37_hidden.clas.abap b/tasks_gen/train/G1103/hidden/t37_hidden.clas.abap new file mode 100644 index 0000000..dbc4caa --- /dev/null +++ b/tasks_gen/train/G1103/hidden/t37_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}t37_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_program TYPE string + iv_vehicle TYPE string + iv_wax TYPE abap_bool DEFAULT abap_false + iv_fleet TYPE abap_bool DEFAULT abap_false + iv_price TYPE decfloat34 DEFAULT 99 + EXPORTING ev_price TYPE decfloat34 + ev_reject TYPE abap_bool + ev_subrc TYPE sysubrc. + METHODS basic_small_price FOR TESTING. + METHODS plus_medium_price FOR TESTING. + METHODS premium_wax_free FOR TESTING. + METHODS fleet_discount_rounds FOR TESTING. + METHODS fleet_rounds_up FOR TESTING. + METHODS large_basic_rejected FOR TESTING. + METHODS invalid_program_raises FOR TESTING. + METHODS invalid_vehicle_raises FOR TESTING. + METHODS reject_flag_false FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t37_hidden IMPLEMENTATION. + METHOD call. + ev_price = iv_price. + CALL FUNCTION '{{P}}WASH_FEE' + EXPORTING + iv_program = iv_program + iv_vehicle = iv_vehicle + iv_extra_wax = iv_wax + iv_fleet = iv_fleet + IMPORTING + ev_reject = ev_reject + CHANGING + cv_price = ev_price + EXCEPTIONS + invalid_program = 1 + invalid_vehicle = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD basic_small_price. + call( EXPORTING iv_program = `BASIC` iv_vehicle = `S` + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8.50' ) act = lv_price ). + ENDMETHOD. + + METHOD plus_medium_price. + call( EXPORTING iv_program = `PLUS` iv_vehicle = `M` + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.00' ) act = lv_price ). + ENDMETHOD. + + METHOD premium_wax_free. + call( EXPORTING iv_program = `PREMIUM` iv_vehicle = `S` iv_wax = abap_true + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.50' ) act = lv_price ). + ENDMETHOD. + + METHOD fleet_discount_rounds. + call( EXPORTING iv_program = `BASIC` iv_vehicle = `S` iv_fleet = abap_true + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '7.23' ) act = lv_price ). + ENDMETHOD. + + METHOD fleet_rounds_up. + call( EXPORTING iv_program = `PREMIUM` iv_vehicle = `S` iv_wax = abap_true + iv_fleet = abap_true + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.73' ) act = lv_price ). + ENDMETHOD. + + METHOD large_basic_rejected. + call( EXPORTING iv_program = `BASIC` iv_vehicle = `L` + IMPORTING ev_price = DATA(lv_price) ev_reject = DATA(lv_reject) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_price ). + ENDMETHOD. + + METHOD invalid_program_raises. + call( EXPORTING iv_program = `GOLD` iv_vehicle = `S` + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD invalid_vehicle_raises. + call( EXPORTING iv_program = `BASIC` iv_vehicle = `XL` + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD reject_flag_false. + call( EXPORTING iv_program = `PLUS` iv_vehicle = `M` + IMPORTING ev_reject = DATA(lv_reject) ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_reject ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1103/mutation.json b/tasks_gen/train/G1103/mutation.json new file mode 100644 index 0000000..838b352 --- /dev/null +++ b/tasks_gen/train/G1103/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1103", + "mutants": [ + { + "object": "{{P}}WASH_FEE", + "mutant": "line 23: AND -> OR (logic)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "BASIC_SMALL_PRICE", + "FLEET_DISCOUNT_ROUNDS", + "FLEET_ROUNDS_UP", + "LARGE_BASIC_REJECTED", + "PLUS_MEDIUM_PRICE", + "PREMIUM_WAX_FREE" + ] + }, + { + "object": "{{P}}WASH_FEE", + "mutant": "line 44: + -> - (arith)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "PLUS_MEDIUM_PRICE" + ] + }, + { + "object": "{{P}}WASH_FEE", + "mutant": "line 49: = -> <> (eq)", + "status": "killed", + "hidden": "6/9", + "failed_tests": [ + "BASIC_SMALL_PRICE", + "FLEET_DISCOUNT_ROUNDS", + "PLUS_MEDIUM_PRICE" + ] + }, + { + "object": "{{P}}WASH_FEE", + "mutant": "line 53: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/9", + "failed_tests": [ + "BASIC_SMALL_PRICE", + "FLEET_DISCOUNT_ROUNDS", + "FLEET_ROUNDS_UP", + "PLUS_MEDIUM_PRICE", + "PREMIUM_WAX_FREE" + ] + }, + { + "object": "{{P}}WASH_FEE", + "mutant": "line 54: 2 -> 3 (const)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "FLEET_DISCOUNT_ROUNDS", + "FLEET_ROUNDS_UP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1103/reference/t37_test.clas.abap b/tasks_gen/train/G1103/reference/t37_test.clas.abap new file mode 100644 index 0000000..8a74656 --- /dev/null +++ b/tasks_gen/train/G1103/reference/t37_test.clas.abap @@ -0,0 +1,101 @@ +CLASS {{p}}t37_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_for + IMPORTING iv_program TYPE string + iv_vehicle TYPE string + iv_wax TYPE abap_bool DEFAULT abap_false + iv_fleet TYPE abap_bool DEFAULT abap_false + EXPORTING ev_price TYPE decfloat34 + ev_reject TYPE abap_bool + ev_subrc TYPE sysubrc. + METHODS base_prices FOR TESTING. + METHODS vehicle_surcharge FOR TESTING. + METHODS extra_wax FOR TESTING. + METHODS fleet_discount FOR TESTING. + METHODS large_basic_rejected FOR TESTING. + METHODS invalid_program FOR TESTING. + METHODS invalid_vehicle FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t37_test IMPLEMENTATION. + METHOD price_for. + ev_price = 0. + CALL FUNCTION '{{P}}WASH_FEE' + EXPORTING + iv_program = iv_program + iv_vehicle = iv_vehicle + iv_extra_wax = iv_wax + iv_fleet = iv_fleet + IMPORTING + ev_reject = ev_reject + CHANGING + cv_price = ev_price + EXCEPTIONS + invalid_program = 1 + invalid_vehicle = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD base_prices. + price_for( EXPORTING iv_program = 'BASIC' iv_vehicle = 'S' + IMPORTING ev_price = DATA(lv_basic) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8.50' ) act = lv_basic ). + + price_for( EXPORTING iv_program = 'PLUS' iv_vehicle = 'S' + IMPORTING ev_price = DATA(lv_plus) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.50' ) act = lv_plus ). + + price_for( EXPORTING iv_program = 'PREMIUM' iv_vehicle = 'S' + IMPORTING ev_price = DATA(lv_premium) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.50' ) act = lv_premium ). + ENDMETHOD. + + METHOD vehicle_surcharge. + price_for( EXPORTING iv_program = 'PLUS' iv_vehicle = 'M' + IMPORTING ev_price = DATA(lv_medium) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.00' ) act = lv_medium ). + + price_for( EXPORTING iv_program = 'PLUS' iv_vehicle = 'L' + IMPORTING ev_price = DATA(lv_large) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.00' ) act = lv_large ). + ENDMETHOD. + + METHOD extra_wax. + price_for( EXPORTING iv_program = 'BASIC' iv_vehicle = 'S' iv_wax = abap_true + IMPORTING ev_price = DATA(lv_basic) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.00' ) act = lv_basic ). + + price_for( EXPORTING iv_program = 'PREMIUM' iv_vehicle = 'S' iv_wax = abap_true + IMPORTING ev_price = DATA(lv_premium) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.50' ) act = lv_premium ). + ENDMETHOD. + + METHOD fleet_discount. + price_for( EXPORTING iv_program = 'BASIC' iv_vehicle = 'S' iv_fleet = abap_true + IMPORTING ev_price = DATA(lv_price) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '7.23' ) act = lv_price ). + ENDMETHOD. + + METHOD large_basic_rejected. + price_for( EXPORTING iv_program = 'BASIC' iv_vehicle = 'L' + IMPORTING ev_price = DATA(lv_price) ev_reject = DATA(lv_reject) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_price ). + ENDMETHOD. + + METHOD invalid_program. + price_for( EXPORTING iv_program = 'GOLD' iv_vehicle = 'S' + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD invalid_vehicle. + price_for( EXPORTING iv_program = 'BASIC' iv_vehicle = 'XL' + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1103/reference/wash_fee.func.abap b/tasks_gen/train/G1103/reference/wash_fee.func.abap new file mode 100644 index 0000000..1c9e74d --- /dev/null +++ b/tasks_gen/train/G1103/reference/wash_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}wash_fee + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + DATA lv_price TYPE decfloat34. + + ev_reject = abap_false. + + IF iv_program <> 'BASIC' AND iv_program <> 'PLUS' AND iv_program <> 'PREMIUM'. + RAISE invalid_program. + ENDIF. + + IF iv_vehicle <> 'S' AND iv_vehicle <> 'M' AND iv_vehicle <> 'L'. + RAISE invalid_vehicle. + ENDIF. + + IF iv_vehicle = 'L' AND iv_program = 'BASIC'. + ev_reject = abap_true. + cv_price = 0. + RETURN. + ENDIF. + + CASE iv_program. + WHEN 'BASIC'. + lv_price = '8.50'. + WHEN 'PLUS'. + lv_price = '12.50'. + WHEN 'PREMIUM'. + lv_price = '18.50'. + ENDCASE. + + CASE iv_vehicle. + WHEN 'M'. + lv_price = lv_price + '2.50'. + WHEN 'L'. + lv_price = lv_price + '5.50'. + ENDCASE. + + IF iv_extra_wax = abap_true AND iv_program <> 'PREMIUM'. + lv_price = lv_price + '3.50'. + ENDIF. + + IF iv_fleet = abap_true. + lv_price = round( val = lv_price * 85 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + cv_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/seed/wash_sample.func.abap b/tasks_gen/train/G1103/seed/wash_sample.func.abap new file mode 100644 index 0000000..e2c904c --- /dev/null +++ b/tasks_gen/train/G1103/seed/wash_sample.func.abap @@ -0,0 +1,17 @@ +FUNCTION {{p}}wash_sample + IMPORTING + iv_program TYPE string + iv_vehicle TYPE string + iv_extra_wax TYPE abap_bool + iv_fleet TYPE abap_bool + EXPORTING + ev_reject TYPE abap_bool + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_program + invalid_vehicle. + + ev_reject = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1103/spec.md b/tasks_gen/train/G1103/spec.md new file mode 100644 index 0000000..217702d --- /dev/null +++ b/tasks_gen/train/G1103/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Calculate the price of a car wash order. The checkout program of the car wash +calls one function module for each order. The function module also tells the +checkout program if the wash tunnel cannot take the vehicle. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_WASH exists in package $TMP. +- Function module {{P}}WASH_SAMPLE exists in this group. It is the sample wash + module. Its interface is the interface that all wash modules must use. + +# 4. Contract +- Create the function module {{P}}WASH_FEE in function group {{P}}FG_WASH. +- Use exactly the interface of {{P}}WASH_SAMPLE: the same parameter names, + kinds, types and exceptions. + +# 5. Business rules +1. If the wash program is not BASIC, PLUS or PREMIUM, raise the exception + INVALID_PROGRAM. +2. If the vehicle category is not S, M or L, raise the exception + INVALID_VEHICLE. Check the wash program (rule 1) before the vehicle + category. +3. Set the reject flag to false, except in rule 5. +4. Base price of the wash program: BASIC 8.50, PLUS 12.50, PREMIUM 18.50. +5. If the vehicle category is L and the wash program is BASIC, the wash tunnel + cannot take the vehicle. Set the reject flag to true and set the price to + 0.00. Rules 6 to 8 do not apply. +6. Vehicle surcharge: category S 0.00, category M 2.50, category L 5.50. Add + the surcharge to the base price. +7. Extra wax: add 3.50 to the price. For the wash program PREMIUM the wax is + included in the price, so add nothing. +8. Fleet discount: if the fleet flag is true, the customer pays 85 % of the + price after rules 4 to 7, rounded half up to 2 decimals. All prices are in + euro. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep every method below 40 statements. Do not + create ATC findings of priority 1 or 2. +- Out of scope: do not change {{P}}WASH_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1103/task.json b/tasks_gen/train/G1103/task.json new file mode 100644 index 0000000..163faf8 --- /dev/null +++ b/tasks_gen/train/G1103/task.json @@ -0,0 +1,88 @@ +{ + "id": "G1103", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_WASH", + "description": "Car wash fee modules" + }, + { + "type": "FUNC", + "name": "{{P}}WASH_SAMPLE", + "functionGroup": "{{P}}FG_WASH", + "file": "seed/wash_sample.func.abap", + "description": "Sample wash module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}WASH_FEE", + "functionGroup": "{{P}}FG_WASH", + "params": [ + { + "name": "iv_program", + "type": "string" + }, + { + "name": "iv_vehicle", + "type": "string" + }, + { + "name": "iv_extra_wax", + "type": "abap_bool" + }, + { + "name": "iv_fleet", + "type": "abap_bool" + }, + { + "name": "ev_reject", + "type": "abap_bool" + }, + { + "name": "cv_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}WASH_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T37_HIDDEN", + "file": "hidden/t37_hidden.clas.abap", + "description": "T37 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}WASH_FEE", + "functionGroup": "{{P}}FG_WASH", + "file": "reference/wash_fee.func.abap", + "description": "Car wash price module" + }, + { + "type": "CLAS", + "name": "{{P}}T37_TEST", + "file": "reference/t37_test.clas.abap", + "description": "T37 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1104/faulty/m0_golf_fee.func.abap b/tasks_gen/train/G1104/faulty/m0_golf_fee.func.abap new file mode 100644 index 0000000..caa1733 --- /dev/null +++ b/tasks_gen/train/G1104/faulty/m0_golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players >= 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 AND iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 6 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes = 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player * 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/faulty/m1_golf_fee.func.abap b/tasks_gen/train/G1104/faulty/m1_golf_fee.func.abap new file mode 100644 index 0000000..5e9239e --- /dev/null +++ b/tasks_gen/train/G1104/faulty/m1_golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players < 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 OR iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 6 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes = 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player * 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/faulty/m2_golf_fee.func.abap b/tasks_gen/train/G1104/faulty/m2_golf_fee.func.abap new file mode 100644 index 0000000..502f7d5 --- /dev/null +++ b/tasks_gen/train/G1104/faulty/m2_golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players < 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 AND iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 7 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes = 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player * 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/faulty/m3_golf_fee.func.abap b/tasks_gen/train/G1104/faulty/m3_golf_fee.func.abap new file mode 100644 index 0000000..4b6d48c --- /dev/null +++ b/tasks_gen/train/G1104/faulty/m3_golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players < 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 AND iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 6 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes <> 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player * 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/faulty/m4_golf_fee.func.abap b/tasks_gen/train/G1104/faulty/m4_golf_fee.func.abap new file mode 100644 index 0000000..af86baf --- /dev/null +++ b/tasks_gen/train/G1104/faulty/m4_golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players < 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 AND iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 6 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes = 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player / 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/generation.json b/tasks_gen/train/G1104/generation.json new file mode 100644 index 0000000..a8ebd46 --- /dev/null +++ b/tasks_gen/train/G1104/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1104", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1104/hidden/t71_hidden.clas.abap b/tasks_gen/train/G1104/hidden/t71_hidden.clas.abap new file mode 100644 index 0000000..02b920a --- /dev/null +++ b/tasks_gen/train/G1104/hidden/t71_hidden.clas.abap @@ -0,0 +1,123 @@ +CLASS {{p}}t71_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + ev_reason TYPE string + ev_message TYPE string. + + METHODS member_18_fee FOR TESTING. + METHODS guest_9_fee FOR TESTING. + METHODS junior_18_fee FOR TESTING. + METHODS twilight_rate FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS twilight_and_group FOR TESTING. + METHODS rejects_player_count FOR TESTING. + METHODS rejects_holes FOR TESTING. + METHODS rejects_category FOR TESTING. + METHODS rejects_tee_hour FOR TESTING. + METHODS exception_message FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t71_hidden IMPLEMENTATION. + METHOD fee_for. + CLEAR: ev_fee, ev_reason, ev_message. + TRY. + CALL FUNCTION '{{P}}GOLF_FEE' + EXPORTING + iv_category = iv_category + iv_holes = iv_holes + iv_players = iv_players + iv_tee_hour = iv_tee_hour + IMPORTING + ev_fee = ev_fee. + CATCH {{p}}cx_golf INTO DATA(lo_exc). + ev_reason = lo_exc->mv_reason. + ev_message = lo_exc->mv_message. + ENDTRY. + ENDMETHOD. + + METHOD member_18_fee. + fee_for( EXPORTING iv_category = 'M' iv_holes = 18 iv_players = 1 iv_tee_hour = 6 + IMPORTING ev_fee = DATA(lv_fee) ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_initial( act = lv_reason ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_fee ). + ENDMETHOD. + + METHOD guest_9_fee. + fee_for( EXPORTING iv_category = 'G' iv_holes = 9 iv_players = 2 iv_tee_hour = 10 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '62.50' ) act = lv_fee ). + ENDMETHOD. + + METHOD junior_18_fee. + fee_for( EXPORTING iv_category = 'J' iv_holes = 18 iv_players = 3 iv_tee_hour = 10 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '37.50' ) act = lv_fee ). + ENDMETHOD. + + METHOD twilight_rate. + fee_for( EXPORTING iv_category = 'G' iv_holes = 18 iv_players = 1 iv_tee_hour = 18 + IMPORTING ev_fee = DATA(lv_late) ). + fee_for( EXPORTING iv_category = 'G' iv_holes = 18 iv_players = 1 iv_tee_hour = 17 + IMPORTING ev_fee = DATA(lv_early) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '46.88' ) act = lv_late ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '62.50' ) act = lv_early ). + ENDMETHOD. + + METHOD group_discount. + fee_for( EXPORTING iv_category = 'G' iv_holes = 18 iv_players = 4 iv_tee_hour = 17 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '225.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD twilight_and_group. + fee_for( EXPORTING iv_category = 'G' iv_holes = 18 iv_players = 4 iv_tee_hour = 19 + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '168.77' ) act = lv_fee ). + ENDMETHOD. + + METHOD rejects_player_count. + fee_for( EXPORTING iv_category = 'M' iv_holes = 18 iv_players = 0 iv_tee_hour = 10 + IMPORTING ev_reason = DATA(lv_zero) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_PLAYERS` act = lv_zero ). + fee_for( EXPORTING iv_category = 'M' iv_holes = 18 iv_players = 5 iv_tee_hour = 10 + IMPORTING ev_reason = DATA(lv_five) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_PLAYERS` act = lv_five ). + ENDMETHOD. + + METHOD rejects_holes. + fee_for( EXPORTING iv_category = 'M' iv_holes = 12 iv_players = 1 iv_tee_hour = 10 + IMPORTING ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_HOLES` act = lv_reason ). + ENDMETHOD. + + METHOD rejects_category. + fee_for( EXPORTING iv_category = 'X' iv_holes = 18 iv_players = 1 iv_tee_hour = 10 + IMPORTING ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_CATEGORY` act = lv_reason ). + ENDMETHOD. + + METHOD rejects_tee_hour. + fee_for( EXPORTING iv_category = 'M' iv_holes = 18 iv_players = 1 iv_tee_hour = 5 + IMPORTING ev_reason = DATA(lv_early) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_TEE_HOUR` act = lv_early ). + fee_for( EXPORTING iv_category = 'M' iv_holes = 18 iv_players = 1 iv_tee_hour = 21 + IMPORTING ev_reason = DATA(lv_late) ). + cl_abap_unit_assert=>assert_equals( exp = `INVALID_TEE_HOUR` act = lv_late ). + ENDMETHOD. + + METHOD exception_message. + fee_for( EXPORTING iv_category = 'M' iv_holes = 12 iv_players = 1 iv_tee_hour = 10 + IMPORTING ev_message = DATA(lv_text) ). + cl_abap_unit_assert=>assert_equals( exp = `Number of holes must be 9 or 18` act = lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1104/mutation.json b/tasks_gen/train/G1104/mutation.json new file mode 100644 index 0000000..a1d4c6f --- /dev/null +++ b/tasks_gen/train/G1104/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1104", + "mutants": [ + { + "object": "{{P}}GOLF_FEE", + "mutant": "line 18: < -> >= (rel)", + "status": "killed", + "hidden": "0/11", + "failed_tests": [ + "EXCEPTION_MESSAGE", + "GROUP_DISCOUNT", + "GUEST_9_FEE", + "JUNIOR_18_FEE", + "MEMBER_18_FEE", + "REJECTS_CATEGORY", + "REJECTS_HOLES", + "REJECTS_PLAYER_COUNT", + "REJECTS_TEE_HOUR", + "TWILIGHT_AND_GROUP", + "TWILIGHT_RATE" + ] + }, + { + "object": "{{P}}GOLF_FEE", + "mutant": "line 25: AND -> OR (logic)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "GUEST_9_FEE", + "JUNIOR_18_FEE", + "MEMBER_18_FEE", + "REJECTS_CATEGORY", + "REJECTS_TEE_HOUR", + "TWILIGHT_AND_GROUP", + "TWILIGHT_RATE" + ] + }, + { + "object": "{{P}}GOLF_FEE", + "mutant": "line 46: 6 -> 7 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "MEMBER_18_FEE" + ] + }, + { + "object": "{{P}}GOLF_FEE", + "mutant": "line 53: = -> <> (eq)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "GROUP_DISCOUNT", + "GUEST_9_FEE", + "JUNIOR_18_FEE", + "MEMBER_18_FEE", + "TWILIGHT_AND_GROUP", + "TWILIGHT_RATE" + ] + }, + { + "object": "{{P}}GOLF_FEE", + "mutant": "line 58: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "TWILIGHT_AND_GROUP", + "TWILIGHT_RATE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1104/reference/cx_golf.clas.abap b/tasks_gen/train/G1104/reference/cx_golf.clas.abap new file mode 100644 index 0000000..0e38851 --- /dev/null +++ b/tasks_gen/train/G1104/reference/cx_golf.clas.abap @@ -0,0 +1,24 @@ +CLASS {{p}}cx_golf DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor + IMPORTING + iv_reason TYPE string + iv_message TYPE string. + + DATA mv_reason TYPE string READ-ONLY. + DATA mv_message TYPE string READ-ONLY. +ENDCLASS. + + +CLASS {{p}}cx_golf IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + mv_message = iv_message. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1104/reference/golf_fee.func.abap b/tasks_gen/train/G1104/reference/golf_fee.func.abap new file mode 100644 index 0000000..9b26355 --- /dev/null +++ b/tasks_gen/train/G1104/reference/golf_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}golf_fee + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + EXPORTING + ev_fee TYPE decfloat34 + RAISING + {{p}}cx_golf. + + CONSTANTS: c_member TYPE decfloat34 VALUE '25.00', + c_guest TYPE decfloat34 VALUE '62.50', + c_junior TYPE decfloat34 VALUE '12.50'. + + DATA lv_per_player TYPE decfloat34. + + IF iv_players < 1 OR iv_players > 4. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_PLAYERS' + iv_message = 'Number of players must be between 1 and 4'. + ENDIF. + + IF iv_holes <> 9 AND iv_holes <> 18. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_HOLES' + iv_message = 'Number of holes must be 9 or 18'. + ENDIF. + + CASE iv_category. + WHEN 'M'. + lv_per_player = c_member. + WHEN 'G'. + lv_per_player = c_guest. + WHEN 'J'. + lv_per_player = c_junior. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_CATEGORY' + iv_message = 'Player category must be M, G or J'. + ENDCASE. + + IF iv_tee_hour < 6 OR iv_tee_hour > 20. + RAISE EXCEPTION TYPE {{p}}cx_golf + EXPORTING + iv_reason = 'INVALID_TEE_HOUR' + iv_message = 'Tee hour must be between 6 and 20'. + ENDIF. + + IF iv_holes = 9. + lv_per_player = lv_per_player / 2. + ENDIF. + + IF iv_tee_hour >= 18. + lv_per_player = round( val = lv_per_player * 75 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + + ev_fee = lv_per_player * iv_players. + + IF iv_players = 4. + ev_fee = round( val = ev_fee * 90 / 100 dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1104/reference/t71_test.clas.abap b/tasks_gen/train/G1104/reference/t71_test.clas.abap new file mode 100644 index 0000000..2b08a50 --- /dev/null +++ b/tasks_gen/train/G1104/reference/t71_test.clas.abap @@ -0,0 +1,78 @@ +CLASS {{p}}t71_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + RETURNING + VALUE(rv_fee) TYPE decfloat34. + + METHODS reason_for + IMPORTING + iv_category TYPE string + iv_holes TYPE i + iv_players TYPE i + iv_tee_hour TYPE i + RETURNING + VALUE(rv_reason) TYPE string. + + METHODS member_fee FOR TESTING. + METHODS twilight_and_group FOR TESTING. + METHODS invalid_holes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t71_test IMPLEMENTATION. + METHOD fee_for. + TRY. + CALL FUNCTION '{{P}}GOLF_FEE' + EXPORTING + iv_category = iv_category + iv_holes = iv_holes + iv_players = iv_players + iv_tee_hour = iv_tee_hour + IMPORTING + ev_fee = rv_fee. + CATCH {{p}}cx_golf. + cl_abap_unit_assert=>fail( msg = 'no exception expected' ). + ENDTRY. + ENDMETHOD. + + METHOD reason_for. + DATA lv_fee TYPE decfloat34. + TRY. + CALL FUNCTION '{{P}}GOLF_FEE' + EXPORTING + iv_category = iv_category + iv_holes = iv_holes + iv_players = iv_players + iv_tee_hour = iv_tee_hour + IMPORTING + ev_fee = lv_fee. + CATCH {{p}}cx_golf INTO DATA(lo_exc). + rv_reason = lo_exc->mv_reason. + ENDTRY. + ENDMETHOD. + + METHOD member_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 25 ) + act = fee_for( iv_category = 'M' iv_holes = 18 iv_players = 1 iv_tee_hour = 6 ) ). + ENDMETHOD. + + METHOD twilight_and_group. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '168.77' ) + act = fee_for( iv_category = 'G' iv_holes = 18 iv_players = 4 iv_tee_hour = 19 ) ). + ENDMETHOD. + + METHOD invalid_holes. + cl_abap_unit_assert=>assert_equals( + exp = `INVALID_HOLES` + act = reason_for( iv_category = 'M' iv_holes = 12 iv_players = 1 iv_tee_hour = 10 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1104/spec.md b/tasks_gen/train/G1104/spec.md new file mode 100644 index 0000000..6839ca6 --- /dev/null +++ b/tasks_gen/train/G1104/spec.md @@ -0,0 +1,73 @@ +# 1. Goal +A golf club prices a tee time booking. The pro shop calls one function module +for each booking before it confirms the booking. The function module rejects an +invalid booking with a class-based exception and returns the green fee of a +valid booking. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_GOLF exists in package $TMP. +- The club sells rounds of 9 holes and rounds of 18 holes. +- The club has three player categories: members, guests and juniors. +- The pro shop calls the function module for each booking. + +# 4. Contract +Exception class {{P}}CX_GOLF in package $TMP: +- The class is public, final and inherits from CX_STATIC_CHECK. +- The class has a public constructor with the importing parameters iv_reason + (type string) and iv_message (type string). +- The class has the public read-only attributes mv_reason (type string) and + mv_message (type string). +- The constructor stores iv_reason in mv_reason and iv_message in mv_message. + +Function module {{P}}GOLF_FEE in function group {{P}}FG_GOLF: +- Importing parameters: + - iv_category type string + - iv_holes type i + - iv_players type i + - iv_tee_hour type i +- Exporting parameter: + - ev_fee type decfloat34 +- The function module raises the class-based exception {{P}}CX_GOLF. Declare + this exception in the interface of the function module, because the caller + handles it with CATCH {{P}}CX_GOLF. +- The function module has no classic exceptions. + +# 5. Business rules +1. The number of players must be between 1 and 4. If it is not, raise + {{P}}CX_GOLF with the reason INVALID_PLAYERS and the message + "Number of players must be between 1 and 4". +2. The number of holes must be 9 or 18. If it is not, raise {{P}}CX_GOLF with + the reason INVALID_HOLES and the message "Number of holes must be 9 or 18". +3. The player category must be M (member), G (guest) or J (junior). If it is + not, raise {{P}}CX_GOLF with the reason INVALID_CATEGORY and the message + "Player category must be M, G or J". +4. The tee hour must be between 6 and 20. If it is not, raise {{P}}CX_GOLF with + the reason INVALID_TEE_HOUR and the message "Tee hour must be between 6 and + 20". +5. Check the rules 1 to 4 in this order. Raise only the first exception that + applies. +6. The green fee per player for 18 holes is 25.00 for a member, 62.50 for a + guest and 12.50 for a junior. For 9 holes the green fee per player is half + of the green fee per player for 18 holes. +7. Twilight rate: if the tee hour is 18 or later, reduce the green fee per + player by 25 %. Round the result half up to 2 decimals. +8. The total fee is the green fee per player times the number of players. +9. Group discount: if the booking has 4 players, reduce the total fee by 10 %. + Round the result half up to 2 decimals. Apply this discount after the + twilight rate. +10. If the booking is valid, return the total fee in ev_fee and raise no + exception. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: none. + +# 7. Acceptance +- The exception class and the function module are active and have no syntax + error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1104/task.json b/tasks_gen/train/G1104/task.json new file mode 100644 index 0000000..df5c257 --- /dev/null +++ b/tasks_gen/train/G1104/task.json @@ -0,0 +1,86 @@ +{ + "id": "G1104", + "category": "D", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_GOLF", + "description": "Golf green fee" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_GOLF", + "description": "Class-based exception for golf bookings" + }, + { + "type": "FUNC", + "name": "{{P}}GOLF_FEE", + "functionGroup": "{{P}}FG_GOLF", + "params": [ + { + "name": "iv_category", + "type": "string" + }, + { + "name": "iv_holes", + "type": "i" + }, + { + "name": "iv_players", + "type": "i" + }, + { + "name": "iv_tee_hour", + "type": "i" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T71_HIDDEN", + "file": "hidden/t71_hidden.clas.abap", + "description": "Hidden tests for {{P}}GOLF_FEE" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_GOLF", + "file": "reference/cx_golf.clas.abap", + "description": "Exception class for golf bookings" + }, + { + "type": "FUNC", + "name": "{{P}}GOLF_FEE", + "functionGroup": "{{P}}FG_GOLF", + "file": "reference/golf_fee.func.abap", + "description": "Green fee calculation" + }, + { + "type": "CLAS", + "name": "{{P}}T71_TEST", + "file": "reference/t71_test.clas.abap", + "description": "Own tests for the function module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1105/faulty/m0_kennel_fee.func.abap b/tasks_gen/train/G1105/faulty/m0_kennel_fee.func.abap new file mode 100644 index 0000000..03cd0d6 --- /dev/null +++ b/tasks_gen/train/G1105/faulty/m0_kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate / iv_nights. + IF iv_peak = abap_true. + cv_fee = cv_fee * '1.2'. + ENDIF. + IF iv_nights >= 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/faulty/m1_kennel_fee.func.abap b/tasks_gen/train/G1105/faulty/m1_kennel_fee.func.abap new file mode 100644 index 0000000..880c530 --- /dev/null +++ b/tasks_gen/train/G1105/faulty/m1_kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_nights. + IF iv_peak <> abap_true. + cv_fee = cv_fee * '1.2'. + ENDIF. + IF iv_nights >= 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/faulty/m2_kennel_fee.func.abap b/tasks_gen/train/G1105/faulty/m2_kennel_fee.func.abap new file mode 100644 index 0000000..14910e4 --- /dev/null +++ b/tasks_gen/train/G1105/faulty/m2_kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_nights. + IF iv_peak = abap_true. + cv_fee = cv_fee / '1.2'. + ENDIF. + IF iv_nights >= 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/faulty/m3_kennel_fee.func.abap b/tasks_gen/train/G1105/faulty/m3_kennel_fee.func.abap new file mode 100644 index 0000000..727acbf --- /dev/null +++ b/tasks_gen/train/G1105/faulty/m3_kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_nights. + IF iv_peak = abap_true. + cv_fee = cv_fee * '1.2'. + ENDIF. + IF iv_nights < 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/faulty/m4_kennel_fee.func.abap b/tasks_gen/train/G1105/faulty/m4_kennel_fee.func.abap new file mode 100644 index 0000000..31288b3 --- /dev/null +++ b/tasks_gen/train/G1105/faulty/m4_kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_nights. + IF iv_peak = abap_true. + cv_fee = cv_fee * '1.2'. + ENDIF. + IF iv_nights >= 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 3 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/generation.json b/tasks_gen/train/G1105/generation.json new file mode 100644 index 0000000..d0fa0ea --- /dev/null +++ b/tasks_gen/train/G1105/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1105", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1105/hidden/t42_hidden.clas.abap b/tasks_gen/train/G1105/hidden/t42_hidden.clas.abap new file mode 100644 index 0000000..d106a61 --- /dev/null +++ b/tasks_gen/train/G1105/hidden/t42_hidden.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}t42_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_pet TYPE string iv_nights TYPE i iv_peak TYPE abap_bool + EXPORTING ev_fee TYPE decfloat34 ev_deposit TYPE decfloat34 ev_subrc TYPE sysubrc. + METHODS dog_peak_night FOR TESTING. + METHODS cat_standard FOR TESTING. + METHODS rabbit_long_peak FOR TESTING. + METHODS dog_long_peak FOR TESTING. + METHODS long_stay_boundary FOR TESTING. + METHODS other_pet_zero FOR TESTING. + METHODS nights_zero_invalid FOR TESTING. + METHODS nights_negative_invalid FOR TESTING. + METHODS deposit_percent FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_hidden IMPLEMENTATION. + METHOD call. + DATA lv_fee TYPE decfloat34 VALUE 99. + CALL FUNCTION '{{P}}KENNEL_FEE' + EXPORTING + iv_pet_type = iv_pet + iv_nights = iv_nights + iv_peak = iv_peak + IMPORTING + ev_deposit = ev_deposit + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ev_fee = lv_fee. + ENDMETHOD. + + METHOD dog_peak_night. + call( EXPORTING iv_pet = `DOG` iv_nights = 1 iv_peak = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '29.88' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.98' ) act = lv_dep ). + ENDMETHOD. + + METHOD cat_standard. + call( EXPORTING iv_pet = `CAT` iv_nights = 3 iv_peak = abap_false + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '53.70' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.74' ) act = lv_dep ). + ENDMETHOD. + + METHOD rabbit_long_peak. + call( EXPORTING iv_pet = `RABBIT` iv_nights = 10 iv_peak = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '128.52' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.70' ) act = lv_dep ). + ENDMETHOD. + + METHOD dog_long_peak. + call( EXPORTING iv_pet = `DOG` iv_nights = 7 iv_peak = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '188.24' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '37.65' ) act = lv_dep ). + ENDMETHOD. + + METHOD long_stay_boundary. + call( EXPORTING iv_pet = `DOG` iv_nights = 6 iv_peak = abap_false + IMPORTING ev_fee = DATA(lv_six) ). + call( EXPORTING iv_pet = `DOG` iv_nights = 7 iv_peak = abap_false + IMPORTING ev_fee = DATA(lv_seven) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '149.40' ) act = lv_six ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '156.87' ) act = lv_seven ). + ENDMETHOD. + + METHOD other_pet_zero. + call( EXPORTING iv_pet = `SNAKE` iv_nights = 5 iv_peak = abap_false + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_dep ). + ENDMETHOD. + + METHOD nights_zero_invalid. + call( EXPORTING iv_pet = `DOG` iv_nights = 0 iv_peak = abap_false + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD nights_negative_invalid. + call( EXPORTING iv_pet = `CAT` iv_nights = -3 iv_peak = abap_false + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD deposit_percent. + call( EXPORTING iv_pet = `CAT` iv_nights = 1 iv_peak = abap_false + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_dep) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '17.90' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.58' ) act = lv_dep ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1105/mutation.json b/tasks_gen/train/G1105/mutation.json new file mode 100644 index 0000000..eb8402c --- /dev/null +++ b/tasks_gen/train/G1105/mutation.json @@ -0,0 +1,69 @@ +{ + "task": "G1105", + "mutants": [ + { + "object": "{{P}}KENNEL_FEE", + "mutant": "line 33: * -> / (arith)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "CAT_STANDARD", + "DOG_LONG_PEAK", + "LONG_STAY_BOUNDARY", + "RABBIT_LONG_PEAK" + ] + }, + { + "object": "{{P}}KENNEL_FEE", + "mutant": "line 34: = -> <> (eq)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "CAT_STANDARD", + "DEPOSIT_PERCENT", + "DOG_LONG_PEAK", + "DOG_PEAK_NIGHT", + "LONG_STAY_BOUNDARY", + "RABBIT_LONG_PEAK" + ] + }, + { + "object": "{{P}}KENNEL_FEE", + "mutant": "line 35: * -> / (arith)", + "status": "killed", + "hidden": "6/9", + "failed_tests": [ + "DOG_LONG_PEAK", + "DOG_PEAK_NIGHT", + "RABBIT_LONG_PEAK" + ] + }, + { + "object": "{{P}}KENNEL_FEE", + "mutant": "line 37: >= -> < (rel)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "CAT_STANDARD", + "DEPOSIT_PERCENT", + "DOG_LONG_PEAK", + "DOG_PEAK_NIGHT", + "LONG_STAY_BOUNDARY", + "RABBIT_LONG_PEAK" + ] + }, + { + "object": "{{P}}KENNEL_FEE", + "mutant": "line 40: 2 -> 3 (const)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "DOG_LONG_PEAK" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1105/reference/kennel_fee.func.abap b/tasks_gen/train/G1105/reference/kennel_fee.func.abap new file mode 100644 index 0000000..029972c --- /dev/null +++ b/tasks_gen/train/G1105/reference/kennel_fee.func.abap @@ -0,0 +1,43 @@ +FUNCTION {{p}}kennel_fee + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + DATA lv_rate TYPE decfloat34. + + ev_deposit = 0. + cv_fee = 0. + + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + CASE iv_pet_type. + WHEN 'DOG'. + lv_rate = '24.90'. + WHEN 'CAT'. + lv_rate = '17.90'. + WHEN 'RABBIT'. + lv_rate = '11.90'. + WHEN OTHERS. + RETURN. + ENDCASE. + + cv_fee = lv_rate * iv_nights. + IF iv_peak = abap_true. + cv_fee = cv_fee * '1.2'. + ENDIF. + IF iv_nights >= 7. + cv_fee = cv_fee * '0.9'. + ENDIF. + cv_fee = round( val = cv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + ev_deposit = round( val = cv_fee * '0.2' dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/reference/t42_test.clas.abap b/tasks_gen/train/G1105/reference/t42_test.clas.abap new file mode 100644 index 0000000..901f7b8 --- /dev/null +++ b/tasks_gen/train/G1105/reference/t42_test.clas.abap @@ -0,0 +1,61 @@ +CLASS {{p}}t42_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_pet TYPE string iv_nights TYPE i iv_peak TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS dog_fee FOR TESTING. + METHODS long_stay FOR TESTING. + METHODS other_pet FOR TESTING. + METHODS invalid_nights FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}KENNEL_FEE' + EXPORTING + iv_pet_type = iv_pet + iv_nights = iv_nights + iv_peak = iv_peak + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD dog_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '74.70' ) + act = fee_for( iv_pet = `DOG` iv_nights = 3 iv_peak = abap_false ) ). + ENDMETHOD. + + METHOD long_stay. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '156.87' ) + act = fee_for( iv_pet = `DOG` iv_nights = 7 iv_peak = abap_false ) ). + ENDMETHOD. + + METHOD other_pet. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 0 ) + act = fee_for( iv_pet = `SNAKE` iv_nights = 4 iv_peak = abap_false ) ). + ENDMETHOD. + + METHOD invalid_nights. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}KENNEL_FEE' + EXPORTING + iv_pet_type = `DOG` + iv_nights = 0 + iv_peak = abap_false + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1105/seed/kennel_sample.func.abap b/tasks_gen/train/G1105/seed/kennel_sample.func.abap new file mode 100644 index 0000000..3f14c51 --- /dev/null +++ b/tasks_gen/train/G1105/seed/kennel_sample.func.abap @@ -0,0 +1,16 @@ +FUNCTION {{p}}kennel_sample + IMPORTING + iv_pet_type TYPE string + iv_nights TYPE i + iv_peak TYPE abap_bool + EXPORTING + ev_deposit TYPE decfloat34 + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + ev_deposit = 0. + cv_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1105/spec.md b/tasks_gen/train/G1105/spec.md new file mode 100644 index 0000000..5a31092 --- /dev/null +++ b/tasks_gen/train/G1105/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A pet boarding kennel charges the owner of a pet for each night that the pet +stays in the kennel. The booking program calls one event module for each +booking. The event module must have the same interface as the sample event +module, because the booking program calls all event modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_KENNEL exists in package $TMP. +- Function module {{P}}KENNEL_SAMPLE exists in this group. It is the sample + event module. Its interface is the interface that all event modules must use. +- The booking program passes the pet type, the number of nights and the peak + season flag. It reads the fee and the deposit. + +# 4. Contract +- Create the function module {{P}}KENNEL_FEE in function group {{P}}FG_KENNEL. +- Use exactly the interface of {{P}}KENNEL_SAMPLE: the same parameter names, + kinds, types and exceptions. + +# 5. Business rules +1. If the number of nights is less than 1, raise the exception INVALID_NIGHTS. + This rule applies before all other rules. +2. The nightly rate is 24.90 for pet type DOG, 17.90 for pet type CAT and + 11.90 for pet type RABBIT. For every other pet type the fee is 0 and the + deposit is 0. +3. The fee is the nightly rate multiplied by the number of nights. +4. If the peak season flag is true, increase the fee by 20 %. +5. If the number of nights is 7 or more, decrease the fee by 10 %. Apply this + decrease after the peak season increase. +6. Round the fee half up to 2 decimals. +7. The deposit is 20 % of the fee, rounded half up to 2 decimals. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}KENNEL_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1105/task.json b/tasks_gen/train/G1105/task.json new file mode 100644 index 0000000..d5e5241 --- /dev/null +++ b/tasks_gen/train/G1105/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1105", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_KENNEL", + "description": "Kennel boarding fee events" + }, + { + "type": "FUNC", + "name": "{{P}}KENNEL_SAMPLE", + "functionGroup": "{{P}}FG_KENNEL", + "file": "seed/kennel_sample.func.abap", + "description": "Sample boarding event module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}KENNEL_FEE", + "functionGroup": "{{P}}FG_KENNEL", + "params": [ + { + "name": "iv_pet_type", + "type": "string" + }, + { + "name": "iv_nights", + "type": "i" + }, + { + "name": "iv_peak", + "type": "abap_bool" + }, + { + "name": "ev_deposit", + "type": "decfloat34" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}KENNEL_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T42_HIDDEN", + "file": "hidden/t42_hidden.clas.abap", + "description": "T42 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}KENNEL_FEE", + "functionGroup": "{{P}}FG_KENNEL", + "file": "reference/kennel_fee.func.abap", + "description": "Boarding fee event module" + }, + { + "type": "CLAS", + "name": "{{P}}T42_TEST", + "file": "reference/t42_test.clas.abap", + "description": "T42 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1106/generation.json b/tasks_gen/train/G1106/generation.json new file mode 100644 index 0000000..eadd211 --- /dev/null +++ b/tasks_gen/train/G1106/generation.json @@ -0,0 +1,37 @@ +{ + "id": "G1106", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/bake_seed.clas.abap line 9: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"INSERT\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "reference/bake_sales.prog.abap line 38: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1106/hidden/bake_hidden.clas.abap b/tasks_gen/train/G1106/hidden/bake_hidden.clas.abap new file mode 100644 index 0000000..bba161d --- /dev/null +++ b/tasks_gen/train/G1106/hidden/bake_hidden.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}bake_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_shop TYPE RANGE OF {{p}}deliv-shop. + METHODS run + IMPORTING iv_date TYPE d + it_shop TYPE tt_shop OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS all_shops_for_date FOR TESTING. + METHODS sorted_by_revenue FOR TESTING. + METHODS totals_and_counts FOR TESTING. + METHODS shop_filter FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS empty_result FOR TESTING. + METHODS tie_break_by_shop FOR TESTING. +ENDCLASS. + +CLASS {{p}}bake_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}bake_sales WITH p_date = iv_date WITH s_shop IN it_shop AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + DESCRIBE TABLE LINES rv_lines. + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + FIELD-SYMBOLS TYPE any. + FIELD-SYMBOLS TYPE any. + DATA lv_text TYPE string. + ASSIGN ir_data->* TO . + READ TABLE ASSIGNING INDEX iv_row. + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'row not found' ). + ASSIGN COMPONENT iv_col OF STRUCTURE TO . + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'column not found' ). + lv_text = . + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_value = lv_text. + ENDMETHOD. + + METHOD all_shops_for_date. + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( run( '20260401' ) ) ). + ENDMETHOD. + + METHOD sorted_by_revenue. + DATA lr_data TYPE REF TO data. + lr_data = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = value_of( ir_data = lr_data iv_row = 1 iv_col = 'SHOP' ) ). + cl_abap_unit_assert=>assert_equals( exp = 'OST' act = value_of( ir_data = lr_data iv_row = 5 iv_col = 'SHOP' ) ). + ENDMETHOD. + + METHOD totals_and_counts. + DATA lr_data TYPE REF TO data. + lr_data = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = value_of( ir_data = lr_data iv_row = 2 iv_col = 'SHOP' ) ). + cl_abap_unit_assert=>assert_equals( exp = '150' act = value_of( ir_data = lr_data iv_row = 2 iv_col = 'LOAVES' ) ). + cl_abap_unit_assert=>assert_equals( exp = '400.00' act = value_of( ir_data = lr_data iv_row = 2 iv_col = 'REVENUE' ) ). + cl_abap_unit_assert=>assert_equals( exp = '2' act = value_of( ir_data = lr_data iv_row = 2 iv_col = 'DELIVERIES' ) ). + ENDMETHOD. + + METHOD shop_filter. + DATA lr_data TYPE REF TO data. + DATA lt_shop TYPE tt_shop. + DATA ls_shop LIKE LINE OF lt_shop. + ls_shop-sign = 'I'. + ls_shop-option = 'EQ'. + ls_shop-low = 'NORD'. + APPEND ls_shop TO lt_shop. + lr_data = run( iv_date = '20260401' it_shop = lt_shop ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = '150' act = value_of( ir_data = lr_data iv_row = 1 iv_col = 'LOAVES' ) ). + cl_abap_unit_assert=>assert_equals( exp = '400.00' act = value_of( ir_data = lr_data iv_row = 1 iv_col = 'REVENUE' ) ). + ENDMETHOD. + + METHOD date_filter. + DATA lr_data TYPE REF TO data. + lr_data = run( '20260331' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = '999' act = value_of( ir_data = lr_data iv_row = 1 iv_col = 'LOAVES' ) ). + cl_abap_unit_assert=>assert_equals( exp = '9980.01' act = value_of( ir_data = lr_data iv_row = 1 iv_col = 'REVENUE' ) ). + ENDMETHOD. + + METHOD empty_result. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( run( '20200101' ) ) ). + ENDMETHOD. + + METHOD tie_break_by_shop. + DATA lr_data TYPE REF TO data. + lr_data = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = 'MITT' act = value_of( ir_data = lr_data iv_row = 3 iv_col = 'SHOP' ) ). + cl_abap_unit_assert=>assert_equals( exp = 'WEST' act = value_of( ir_data = lr_data iv_row = 4 iv_col = 'SHOP' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1106/reference/bake_sales.prog.abap b/tasks_gen/train/G1106/reference/bake_sales.prog.abap new file mode 100644 index 0000000..ba98b49 --- /dev/null +++ b/tasks_gen/train/G1106/reference/bake_sales.prog.abap @@ -0,0 +1,173 @@ +REPORT {{p}}bake_sales. + +DATA gv_shop TYPE {{p}}deliv-shop. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_raw, + shop TYPE {{p}}deliv-shop, + loaves TYPE {{p}}deliv-loaves, + unit_price TYPE {{p}}deliv-unit_price, + END OF ty_raw. + TYPES tt_raw TYPE STANDARD TABLE OF ty_raw. + + TYPES: BEGIN OF ty_line, + shop TYPE {{p}}deliv-shop, + loaves TYPE i, + revenue TYPE {{p}}deliv-unit_price, + deliveries TYPE i, + END OF ty_line. + TYPES tt_line TYPE STANDARD TABLE OF ty_line. + + METHODS aggregate + IMPORTING it_raw TYPE tt_raw + CHANGING ct_lines TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD aggregate. + DATA lt_sorted TYPE tt_raw. + DATA ls_raw TYPE ty_raw. + DATA ls_line TYPE ty_line. + + CLEAR ct_lines. + lt_sorted = it_raw. + SORT lt_sorted BY shop. + + LOOP AT lt_sorted INTO ls_raw. + AT NEW shop. + CLEAR ls_line. + ls_line-shop = ls_raw-shop. + ENDAT. + ls_line-loaves = ls_line-loaves + ls_raw-loaves. + ls_line-revenue = ls_line-revenue + ls_raw-loaves * ls_raw-unit_price. + ls_line-deliveries = ls_line-deliveries + 1. + AT END OF shop. + APPEND ls_line TO ct_lines. + ENDAT. + ENDLOOP. + + SORT ct_lines BY revenue DESCENDING shop ASCENDING. + ENDMETHOD. + + METHOD display. + DATA lo_alv TYPE REF TO cl_salv_table. + DATA lx_error TYPE REF TO cx_salv_msg. + + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lx_error. + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +DATA go_report TYPE REF TO lcl_report. +DATA gt_raw TYPE lcl_report=>tt_raw. +DATA gt_lines TYPE lcl_report=>tt_line. + +START-OF-SELECTION. + SELECT * FROM {{p}}deliv + WHERE deliv_date = p_date + AND shop IN s_shop + INTO CORRESPONDING FIELDS OF TABLE gt_raw. + + CREATE OBJECT go_report. + go_report->aggregate( EXPORTING it_raw = gt_raw CHANGING ct_lines = gt_lines ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS add_raw + IMPORTING iv_shop TYPE {{p}}deliv-shop + iv_loaves TYPE i + iv_price TYPE {{p}}deliv-unit_price + CHANGING ct_raw TYPE lcl_report=>tt_raw. + METHODS sums_one_shop FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. + METHODS keeps_shops_apart FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD add_raw. + DATA ls_raw TYPE lcl_report=>ty_raw. + ls_raw-shop = iv_shop. + ls_raw-loaves = iv_loaves. + ls_raw-unit_price = iv_price. + APPEND ls_raw TO ct_raw. + ENDMETHOD. + + METHOD sums_one_shop. + DATA lt_raw TYPE lcl_report=>tt_raw. + DATA lt_lines TYPE lcl_report=>tt_line. + DATA ls_line TYPE lcl_report=>ty_line. + DATA lo_report TYPE REF TO lcl_report. + DATA lv_exp TYPE {{p}}deliv-unit_price. + DATA lv_count TYPE i. + + add_raw( EXPORTING iv_shop = 'NORD' iv_loaves = 10 iv_price = '2.00' + CHANGING ct_raw = lt_raw ). + add_raw( EXPORTING iv_shop = 'NORD' iv_loaves = 5 iv_price = '4.00' + CHANGING ct_raw = lt_raw ). + + CREATE OBJECT lo_report. + lo_report->aggregate( EXPORTING it_raw = lt_raw CHANGING ct_lines = lt_lines ). + + DESCRIBE TABLE lt_lines LINES lv_count. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ). + READ TABLE lt_lines INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = ls_line-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-deliveries ). + lv_exp = '40.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_line-revenue ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA lt_raw TYPE lcl_report=>tt_raw. + DATA lt_lines TYPE lcl_report=>tt_line. + DATA ls_line TYPE lcl_report=>ty_line. + DATA lo_report TYPE REF TO lcl_report. + DATA lv_exp TYPE {{p}}deliv-shop. + + add_raw( EXPORTING iv_shop = 'A' iv_loaves = 10 iv_price = '1.00' + CHANGING ct_raw = lt_raw ). + add_raw( EXPORTING iv_shop = 'B' iv_loaves = 10 iv_price = '9.00' + CHANGING ct_raw = lt_raw ). + + CREATE OBJECT lo_report. + lo_report->aggregate( EXPORTING it_raw = lt_raw CHANGING ct_lines = lt_lines ). + + READ TABLE lt_lines INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + lv_exp = 'B'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_line-shop ). + ENDMETHOD. + + METHOD keeps_shops_apart. + DATA lt_raw TYPE lcl_report=>tt_raw. + DATA lt_lines TYPE lcl_report=>tt_line. + DATA lo_report TYPE REF TO lcl_report. + DATA lv_count TYPE i. + + add_raw( EXPORTING iv_shop = 'A' iv_loaves = 1 iv_price = '1.00' + CHANGING ct_raw = lt_raw ). + add_raw( EXPORTING iv_shop = 'B' iv_loaves = 1 iv_price = '1.00' + CHANGING ct_raw = lt_raw ). + + CREATE OBJECT lo_report. + lo_report->aggregate( EXPORTING it_raw = lt_raw CHANGING ct_lines = lt_lines ). + + DESCRIBE TABLE lt_lines LINES lv_count. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1106/seed/bake_seed.clas.abap b/tasks_gen/train/G1106/seed/bake_seed.clas.abap new file mode 100644 index 0000000..3376e58 --- /dev/null +++ b/tasks_gen/train/G1106/seed/bake_seed.clas.abap @@ -0,0 +1,57 @@ +CLASS {{p}}bake_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PRIVATE SECTION. + TYPES tt_deliv TYPE STANDARD TABLE OF {{p}}deliv. + METHODS add_delivery + IMPORTING iv_id TYPE {{p}}deliv-deliv_id + iv_shop TYPE {{p}}deliv-shop + iv_date TYPE {{p}}deliv-deliv_date + iv_loaves TYPE {{p}}deliv-loaves + iv_price TYPE {{p}}deliv-unit_price + CHANGING ct_deliv TYPE tt_deliv. +ENDCLASS. + +CLASS {{p}}bake_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_deliv TYPE tt_deliv. + + DELETE FROM {{p}}deliv. + + add_delivery( EXPORTING iv_id = 1 iv_shop = 'NORD' iv_date = '20260401' + iv_loaves = 100 iv_price = '2.50' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 2 iv_shop = 'NORD' iv_date = '20260401' + iv_loaves = 50 iv_price = '3.00' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 3 iv_shop = 'SUED' iv_date = '20260401' + iv_loaves = 200 iv_price = '3.00' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 4 iv_shop = 'WEST' iv_date = '20260401' + iv_loaves = 40 iv_price = '5.00' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 5 iv_shop = 'MITT' iv_date = '20260401' + iv_loaves = 80 iv_price = '2.50' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 6 iv_shop = 'OST' iv_date = '20260401' + iv_loaves = 10 iv_price = '2.50' + CHANGING ct_deliv = lt_deliv ). + add_delivery( EXPORTING iv_id = 7 iv_shop = 'NORD' iv_date = '20260331' + iv_loaves = 999 iv_price = '9.99' + CHANGING ct_deliv = lt_deliv ). + + INSERT {{p}}deliv FROM TABLE lt_deliv. + out->write( sy-dbcnt ). + ENDMETHOD. + + METHOD add_delivery. + DATA ls_deliv TYPE {{p}}deliv. + + ls_deliv-deliv_id = iv_id. + ls_deliv-shop = iv_shop. + ls_deliv-deliv_date = iv_date. + ls_deliv-loaves = iv_loaves. + ls_deliv-unit_price = iv_price. + APPEND ls_deliv TO ct_deliv. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1106/seed/deliv.tabl.asabap b/tasks_gen/train/G1106/seed/deliv.tabl.asabap new file mode 100644 index 0000000..7df9958 --- /dev/null +++ b/tasks_gen/train/G1106/seed/deliv.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Bread deliveries' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}deliv { + key client : abap.clnt not null; + key deliv_id : abap.int4 not null; + shop : abap.char(4); + deliv_date : abap.dats; + loaves : abap.int4; + unit_price : abap.dec(13,2); +} diff --git a/tasks_gen/train/G1106/spec.md b/tasks_gen/train/G1106/spec.md new file mode 100644 index 0000000..e85525c --- /dev/null +++ b/tasks_gen/train/G1106/spec.md @@ -0,0 +1,49 @@ +# 1. Goal +Show the bread sales of each shop for one delivery day in an ALV list. The +bakery manager uses the list to compare the shops. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DELIV exists in package $TMP and contains data. + Fields: DELIV_ID (INT4, key), SHOP (CHAR 4), DELIV_DATE (DATS), + LOAVES (INT4), UNIT_PRICE (DEC 13,2). +- One row of {{P}}DELIV is one delivery of bread to one shop on one day. + LOAVES is the number of loaves in this delivery. UNIT_PRICE is the price of + one loaf in this delivery. + +# 4. Contract +- Create the executable program {{P}}BAKE_SALES in package $TMP. +- Selection screen: + - Parameter P_DATE, type DATS, mandatory: the delivery day. + - Select-option S_SHOP for the shop. +- Show the result with CL_SALV_TABLE. The output table has these columns with + these names: + - SHOP: the shop. + - LOAVES: the number of loaves. + - REVENUE: the revenue. + - DELIVERIES: the number of deliveries. + +# 5. Business rules +1. Include a delivery only if DELIV_DATE is equal to P_DATE and SHOP is in + S_SHOP. An empty S_SHOP includes every shop. +2. Show one line for each shop that has at least one included delivery. +3. LOAVES is the sum of the LOAVES of the included deliveries of the shop. +4. REVENUE is the sum of LOAVES multiplied by UNIT_PRICE of the included + deliveries of the shop. +5. DELIVERIES is the number of included deliveries of the shop. +6. Sort the lines by REVENUE, the highest revenue first. If two shops have the + same REVENUE, the shop with the smaller SHOP value comes first. +7. If no delivery is included, show an empty list. + +# 6. Constraints +- Release target: 7.02. Use only language elements that exist in release 7.02. + Do not use inline declarations, constructor expressions, or table expressions. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}DELIV. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1106/task.json b/tasks_gen/train/G1106/task.json new file mode 100644 index 0000000..3449b7d --- /dev/null +++ b/tasks_gen/train/G1106/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1106", + "category": "G", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v702", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DELIV", + "file": "seed/deliv.tabl.asabap", + "description": "Bread deliveries" + }, + { + "type": "CLAS", + "name": "{{P}}BAKE_SEED", + "file": "seed/bake_seed.clas.abap", + "description": "Bread delivery seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}BAKE_SALES", + "parameters": [ + "p_date", + "s_shop" + ] + } + ], + "out_of_scope": [ + "{{P}}DELIV" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}BAKE_HIDDEN", + "file": "hidden/bake_hidden.clas.abap", + "description": "Hidden tests for {{P}}BAKE_SALES" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}BAKE_SALES", + "file": "reference/bake_sales.prog.abap", + "description": "Bread sales per shop and day" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1107/generation.json b/tasks_gen/train/G1107/generation.json new file mode 100644 index 0000000..5bfcd3b --- /dev/null +++ b/tasks_gen/train/G1107/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1107", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1107/reference/lkr_fee.func.abap b/tasks_gen/train/G1107/reference/lkr_fee.func.abap new file mode 100644 index 0000000..a21a595 --- /dev/null +++ b/tasks_gen/train/G1107/reference/lkr_fee.func.abap @@ -0,0 +1,34 @@ +FUNCTION {{p}}lkr_fee + IMPORTING + iv_size_code TYPE string + iv_days TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_size_code. + WHEN 'S'. + lv_rate = '2.50'. + WHEN 'M'. + lv_rate = '4.25'. + WHEN 'L'. + lv_rate = '6.75'. + WHEN OTHERS. + " The specification gives no rule for a size code that is not S, M or L. + RETURN. + ENDCASE. + + ev_fee = lv_rate * iv_days. + IF iv_member = abap_true. + ev_fee = ev_fee / 2. + ENDIF. + ev_fee = round( val = ev_fee dec = 2 mode = cl_abap_math=>round_half_up ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1107/reference/lkr_test.clas.abap b/tasks_gen/train/G1107/reference/lkr_test.clas.abap new file mode 100644 index 0000000..dbf6397 --- /dev/null +++ b/tasks_gen/train/G1107/reference/lkr_test.clas.abap @@ -0,0 +1,55 @@ +CLASS {{p}}lkr_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_size_code TYPE string + iv_days TYPE i + iv_member TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS daily_rate FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS invalid_days FOR TESTING. +ENDCLASS. + + +CLASS {{p}}lkr_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}LKR_FEE' + EXPORTING + iv_size_code = iv_size_code + iv_days = iv_days + iv_member = iv_member + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD daily_rate. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '12.75' ) + act = fee_for( iv_size_code = 'M' iv_days = 3 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD member_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '6.38' ) + act = fee_for( iv_size_code = 'M' iv_days = 3 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD invalid_days. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}LKR_FEE' + EXPORTING + iv_size_code = 'S' + iv_days = 0 + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1107/seed/lkr_sample.func.abap b/tasks_gen/train/G1107/seed/lkr_sample.func.abap new file mode 100644 index 0000000..c40cc24 --- /dev/null +++ b/tasks_gen/train/G1107/seed/lkr_sample.func.abap @@ -0,0 +1,17 @@ +FUNCTION {{p}}lkr_sample + IMPORTING + iv_size_code TYPE string + iv_days TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + ev_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1107/spec.md b/tasks_gen/train/G1107/spec.md new file mode 100644 index 0000000..3b6fbaf --- /dev/null +++ b/tasks_gen/train/G1107/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A ski resort rents lockers in its ski depot. The depot terminal calls one +function module for each locker rental. The function module calculates the fee +of the rental. The terminal calls all locker modules in the same way, so the new +function module must have the same interface as the sample module. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_LKR exists in package $TMP. +- Function module {{P}}LKR_SAMPLE exists in this group. It is the sample locker + module. Its interface is the interface that all locker modules must use. +- The size code of a locker is stored in the electronic lock of the locker. The + terminal sends the code that the lock reports. +- The depot has lockers of the three types S, M and L. Locks of retired locker + types are still in use in the old part of the depot. These locks report the + codes of their retired types. + +# 4. Contract +- Create the function module {{P}}LKR_FEE in function group {{P}}FG_LKR. +- Use exactly the interface of {{P}}LKR_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the number of days is less than 1, raise the exception INVALID_DAYS. +2. The daily rate of a locker depends on its size code: + - Size code S: 2.50 per day. + - Size code M: 4.25 per day. + - Size code L: 6.75 per day. +3. The fee is the daily rate multiplied by the number of days. +4. A member of the ski club pays 50 % of the fee. +5. Round the fee half up to 2 decimals. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}LKR_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1107/task.json b/tasks_gen/train/G1107/task.json new file mode 100644 index 0000000..7781c56 --- /dev/null +++ b/tasks_gen/train/G1107/task.json @@ -0,0 +1,90 @@ +{ + "id": "G1107", + "category": "H", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The specification gives no rule for a locker size code that is not S, M or L, so the fee of such a rental cannot be determined.", + "keywords": [ + [ + "unknown", + "code" + ], + [ + "size", + "code" + ], + [ + "missing", + "rule" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_LKR", + "description": "Locker fee function group" + }, + { + "type": "FUNC", + "name": "{{P}}LKR_SAMPLE", + "functionGroup": "{{P}}FG_LKR", + "file": "seed/lkr_sample.func.abap", + "description": "Sample locker module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}LKR_FEE", + "functionGroup": "{{P}}FG_LKR", + "params": [ + { + "name": "iv_size_code", + "type": "string" + }, + { + "name": "iv_days", + "type": "i" + }, + { + "name": "iv_member", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}LKR_SAMPLE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}LKR_FEE", + "functionGroup": "{{P}}FG_LKR", + "file": "reference/lkr_fee.func.abap", + "description": "Locker fee module" + }, + { + "type": "CLAS", + "name": "{{P}}LKR_TEST", + "file": "reference/lkr_test.clas.abap", + "description": "Own tests for the locker fee module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1108/generation.json b/tasks_gen/train/G1108/generation.json new file mode 100644 index 0000000..ae1bf0f --- /dev/null +++ b/tasks_gen/train/G1108/generation.json @@ -0,0 +1,50 @@ +{ + "id": "G1108", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 89: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 91: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1108/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1108/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..f9a916f --- /dev/null +++ b/tasks_gen/train/G1108/hidden/t31_hidden.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS rows_only_long FOR TESTING. + METHODS short_lesson_hidden FOR TESTING. + METHODS instr_id_shown FOR TESTING. + METHODS date_shown FOR TESTING. + METHODS duration_shown FOR TESTING. + METHODS fee_shown FOR TESTING. + METHODS instructor_name FOR TESTING. + METHODS instructor_city FOR TESTING. + METHODS no_instructor_shown FOR TESTING. + METHODS no_instructor_empty FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_LESSON_INSTR' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_instr TYPE STANDARD TABLE OF {{p}}instr WITH EMPTY KEY. + DATA lt_lesson TYPE STANDARD TABLE OF {{p}}lesson WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_instr = VALUE #( ( instr_id = 'I1' full_name = 'ANNA' city = 'BERLIN' ) + ( instr_id = 'I2' full_name = 'BEN' city = 'HAMBURG' ) ). + lt_lesson = VALUE #( ( lesson_id = 'L1' instr_id = 'I1' lesson_date = '20240110' + duration = 45 fee = '50.00' ) + ( lesson_id = 'L2' instr_id = 'I1' lesson_date = '20240111' + duration = 90 fee = '95.00' ) + ( lesson_id = 'L3' instr_id = 'I2' lesson_date = '20240112' + duration = 30 fee = '35.00' ) + ( lesson_id = 'L4' instr_id = 'I9' lesson_date = '20240113' + duration = 60 fee = '70.00' ) ). + go_env->insert_test_data( lt_instr ). + go_env->insert_test_data( lt_lesson ). + ENDMETHOD. + + METHOD rows_only_long. + SELECT COUNT(*) FROM {{p}}i_lesson_instr INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD short_lesson_hidden. + SELECT SINGLE lesson_id FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L3' INTO @DATA(lv_lesson_id). + cl_abap_unit_assert=>assert_initial( act = lv_lesson_id ). + ENDMETHOD. + + METHOD instr_id_shown. + SELECT SINGLE instr_id FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L1' INTO @DATA(lv_instr_id). + cl_abap_unit_assert=>assert_equals( exp = 'I1' act = lv_instr_id ). + ENDMETHOD. + + METHOD date_shown. + SELECT SINGLE lesson_date FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L1' INTO @DATA(lv_date). + cl_abap_unit_assert=>assert_equals( exp = CONV dats( '20240110' ) act = lv_date ). + ENDMETHOD. + + METHOD duration_shown. + SELECT SINGLE duration FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L2' INTO @DATA(lv_duration). + cl_abap_unit_assert=>assert_equals( exp = 90 act = lv_duration ). + ENDMETHOD. + + METHOD fee_shown. + SELECT SINGLE fee FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L2' INTO @DATA(lv_fee). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '95.00' ) + act = CONV decfloat34( lv_fee ) ). + ENDMETHOD. + + METHOD instructor_name. + SELECT SINGLE instructor_name FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L1' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'ANNA' act = lv_name ). + ENDMETHOD. + + METHOD instructor_city. + SELECT SINGLE instructor_city FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L2' INTO @DATA(lv_city). + cl_abap_unit_assert=>assert_equals( exp = 'BERLIN' act = lv_city ). + ENDMETHOD. + + METHOD no_instructor_shown. + SELECT SINGLE lesson_id FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L4' INTO @DATA(lv_lesson_id). + cl_abap_unit_assert=>assert_equals( exp = 'L4' act = lv_lesson_id ). + ENDMETHOD. + + METHOD no_instructor_empty. + SELECT SINGLE instructor_name FROM {{p}}i_lesson_instr + WHERE lesson_id = 'L4' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_initial( act = lv_name ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1108/reference/i_lesson_instr.ddls.asddls b/tasks_gen/train/G1108/reference/i_lesson_instr.ddls.asddls new file mode 100644 index 0000000..e603310 --- /dev/null +++ b/tasks_gen/train/G1108/reference/i_lesson_instr.ddls.asddls @@ -0,0 +1,17 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Lessons with instructor' +define view entity {{P}}I_LESSON_INSTR + as select from {{p}}lesson as lesson + association [1..1] to {{p}}instr as _instructor + on lesson.instr_id = _instructor.instr_id +{ + key lesson.lesson_id as lesson_id, + lesson.instr_id as instr_id, + lesson.lesson_date as lesson_date, + lesson.duration as duration, + lesson.fee as fee, + _instructor.full_name as instructor_name, + _instructor.city as instructor_city, + _instructor +} +where lesson.duration >= 45 diff --git a/tasks_gen/train/G1108/reference/t31_test.clas.abap b/tasks_gen/train/G1108/reference/t31_test.clas.abap new file mode 100644 index 0000000..11af4c1 --- /dev/null +++ b/tasks_gen/train/G1108/reference/t31_test.clas.abap @@ -0,0 +1,55 @@ +CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS lesson_with_instructor FOR TESTING. + METHODS lesson_without_instructor FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_LESSON_INSTR' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_instr TYPE STANDARD TABLE OF {{p}}instr WITH EMPTY KEY. + DATA lt_lesson TYPE STANDARD TABLE OF {{p}}lesson WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_instr = VALUE #( ( instr_id = 'A' full_name = 'CARA' city = 'KOELN' ) ). + lt_lesson = VALUE #( ( lesson_id = 'X1' instr_id = 'A' lesson_date = '20240201' + duration = 45 fee = '40.00' ) + ( lesson_id = 'X2' instr_id = 'A' lesson_date = '20240202' + duration = 20 fee = '20.00' ) + ( lesson_id = 'X3' instr_id = 'Z' lesson_date = '20240203' + duration = 60 fee = '30.00' ) ). + go_env->insert_test_data( lt_instr ). + go_env->insert_test_data( lt_lesson ). + ENDMETHOD. + + METHOD lesson_with_instructor. + SELECT SINGLE lesson_id, instructor_name, instructor_city + FROM {{p}}i_lesson_instr + WHERE lesson_id = 'X1' INTO @DATA(ls_result). + cl_abap_unit_assert=>assert_equals( exp = 'X1' act = ls_result-lesson_id ). + cl_abap_unit_assert=>assert_equals( exp = 'CARA' act = ls_result-instructor_name ). + cl_abap_unit_assert=>assert_equals( exp = 'KOELN' act = ls_result-instructor_city ). + ENDMETHOD. + + METHOD lesson_without_instructor. + SELECT SINGLE lesson_id, instructor_name + FROM {{p}}i_lesson_instr + WHERE lesson_id = 'X3' INTO @DATA(ls_result). + cl_abap_unit_assert=>assert_equals( exp = 'X3' act = ls_result-lesson_id ). + cl_abap_unit_assert=>assert_initial( act = ls_result-instructor_name ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1108/seed/instr.tabl.asabap b/tasks_gen/train/G1108/seed/instr.tabl.asabap new file mode 100644 index 0000000..c6ccd11 --- /dev/null +++ b/tasks_gen/train/G1108/seed/instr.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Instructor' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}instr { + key client : abap.clnt not null; + key instr_id : abap.char(10) not null; + full_name : abap.char(30); + city : abap.char(20); +} diff --git a/tasks_gen/train/G1108/seed/lesson.tabl.asabap b/tasks_gen/train/G1108/seed/lesson.tabl.asabap new file mode 100644 index 0000000..95a0e10 --- /dev/null +++ b/tasks_gen/train/G1108/seed/lesson.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Lesson' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}lesson { + key client : abap.clnt not null; + key lesson_id : abap.char(10) not null; + instr_id : abap.char(10); + lesson_date : abap.dats; + duration : abap.int4; + fee : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1108/spec.md b/tasks_gen/train/G1108/spec.md new file mode 100644 index 0000000..4542494 --- /dev/null +++ b/tasks_gen/train/G1108/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A driving school shows its lessons together with the instructor of each lesson. +A report and an OData service read the data. The data must be prepared in the +database, in a CDS view. The view exposes the instructor data of a lesson +through an association. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}INSTR (instructor), package $TMP. Key: INSTR_ID (CHAR 10). + Fields: FULL_NAME (CHAR 30), CITY (CHAR 20). +- Table {{P}}LESSON (lesson), package $TMP. Key: LESSON_ID (CHAR 10). + Fields: INSTR_ID (CHAR 10), LESSON_DATE (DATS), DURATION (INT4, in minutes), + FEE (DEC 15,2). + +# 4. Contract +- Create the CDS view entity {{P}}I_LESSON_INSTR in package $TMP. +- Elements, with these names: LESSON_ID (key), INSTR_ID, LESSON_DATE, DURATION, + FEE, INSTRUCTOR_NAME, INSTRUCTOR_CITY, and the association _INSTRUCTOR. +- The association _INSTRUCTOR points to the table {{P}}INSTR. Its ON condition + compares the INSTR_ID of the lesson with the INSTR_ID of the instructor. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each lesson that lasts at least 45 minutes. Do not + show a lesson that is shorter than 45 minutes. +2. LESSON_ID, INSTR_ID, LESSON_DATE, DURATION and FEE are the values of the + lesson. +3. INSTRUCTOR_NAME is the FULL_NAME of the instructor whose INSTR_ID is the + INSTR_ID of the lesson. INSTRUCTOR_CITY is the CITY of that instructor. +4. A lesson whose instructor does not exist is still shown. INSTRUCTOR_NAME and + INSTRUCTOR_CITY of that lesson are empty. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}INSTR and {{P}}LESSON. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1108/task.json b/tasks_gen/train/G1108/task.json new file mode 100644 index 0000000..b91a70b --- /dev/null +++ b/tasks_gen/train/G1108/task.json @@ -0,0 +1,69 @@ +{ + "id": "G1108", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}INSTR", + "file": "seed/instr.tabl.asabap", + "description": "Instructor" + }, + { + "type": "TABL", + "name": "{{P}}LESSON", + "file": "seed/lesson.tabl.asabap", + "description": "Lesson" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_LESSON_INSTR", + "fields": [ + "LESSON_ID", + "INSTR_ID", + "LESSON_DATE", + "DURATION", + "FEE", + "INSTRUCTOR_NAME", + "INSTRUCTOR_CITY", + "_INSTRUCTOR" + ] + } + ], + "out_of_scope": [ + "{{P}}INSTR", + "{{P}}LESSON" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_LESSON_INSTR", + "file": "reference/i_lesson_instr.ddls.asddls", + "description": "Lessons with instructor" + }, + { + "type": "CLAS", + "name": "{{P}}T31_TEST", + "file": "reference/t31_test.clas.abap", + "description": "T31 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1109/generation.json b/tasks_gen/train/G1109/generation.json new file mode 100644 index 0000000..7499011 --- /dev/null +++ b/tasks_gen/train/G1109/generation.json @@ -0,0 +1,40 @@ +{ + "id": "G1109", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/call.tabl.ddl: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/call.tabl.ddl: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "seed/chg.tabl.ddl: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/chg.tabl.ddl: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 5451 (char 5450)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1109/hidden/tel_hidden.clas.abap b/tasks_gen/train/G1109/hidden/tel_hidden.clas.abap new file mode 100644 index 0000000..52206e9 --- /dev/null +++ b/tasks_gen/train/G1109/hidden/tel_hidden.clas.abap @@ -0,0 +1,129 @@ +CLASS {{p}}tel_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS rate + IMPORTING iv_ctype TYPE string + iv_zone TYPE string + iv_secs TYPE i + EXPORTING ev_minutes TYPE i + ev_charge TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS zero_duration FOR TESTING. + METHODS one_minute FOR TESTING. + METHODS rounds_up FOR TESTING. + METHODS rounds_half_up FOR TESTING. + METHODS zone_b FOR TESTING. + METHODS mobile_zone_c FOR TESTING. + METHODS special_zone_a FOR TESTING. + METHODS unknown_ctype FOR TESTING. + METHODS unknown_zone FOR TESTING. + METHODS negative_secs FOR TESTING. + METHODS long_call FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tel_hidden IMPLEMENTATION. + METHOD rate. + CALL FUNCTION '{{P}}TEL_RATE' + EXPORTING + iv_ctype = iv_ctype + iv_zone = iv_zone + iv_secs = iv_secs + IMPORTING + ev_minutes = ev_minutes + ev_charge = ev_charge + EXCEPTIONS + invalid_duration = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD zero_duration. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = 0 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD one_minute. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = 30 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.12' ) act = lv_charge ). + ENDMETHOD. + + METHOD rounds_up. + rate( EXPORTING iv_ctype = 'IT' iv_zone = 'B' iv_secs = 61 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.81' ) act = lv_charge ). + ENDMETHOD. + + METHOD rounds_half_up. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = 120 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.21' ) act = lv_charge ). + ENDMETHOD. + + METHOD zone_b. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'B' iv_secs = 60 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.22' ) act = lv_charge ). + ENDMETHOD. + + METHOD mobile_zone_c. + rate( EXPORTING iv_ctype = 'MO' iv_zone = 'C' iv_secs = 1 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.72' ) act = lv_charge ). + ENDMETHOD. + + METHOD special_zone_a. + rate( EXPORTING iv_ctype = 'SP' iv_zone = 'A' iv_secs = 180 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.20' ) act = lv_charge ). + ENDMETHOD. + + METHOD unknown_ctype. + rate( EXPORTING iv_ctype = 'XX' iv_zone = 'A' iv_secs = 60 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD unknown_zone. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'Z' iv_secs = 60 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD negative_secs. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = -5 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD long_call. + rate( EXPORTING iv_ctype = 'IT' iv_zone = 'C' iv_secs = 3600 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 60 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.43' ) act = lv_charge ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1109/reference/tel_rate.func.abap b/tasks_gen/train/G1109/reference/tel_rate.func.abap new file mode 100644 index 0000000..c35c6fe --- /dev/null +++ b/tasks_gen/train/G1109/reference/tel_rate.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}tel_rate + IMPORTING + iv_ctype TYPE string + iv_zone TYPE string + iv_secs TYPE i + EXPORTING + ev_minutes TYPE i + ev_charge TYPE decfloat34 + EXCEPTIONS + invalid_duration. + + TYPES: BEGIN OF ty_price, + ctype TYPE string, + zone TYPE string, + price TYPE decfloat34, + END OF ty_price, + tt_price TYPE SORTED TABLE OF ty_price WITH UNIQUE KEY ctype zone. + + DATA lt_prices TYPE tt_price. + DATA ls_price TYPE ty_price. + + IF iv_secs < 0. + RAISE invalid_duration. + ENDIF. + + IF iv_secs > 0. + ev_minutes = ( iv_secs + 59 ) DIV 60. + + lt_prices = VALUE #( + ( ctype = 'NT' zone = 'A' price = '0.09' ) + ( ctype = 'NT' zone = 'B' price = '0.19' ) + ( ctype = 'NT' zone = 'C' price = '0.29' ) + ( ctype = 'IT' zone = 'A' price = '0.19' ) + ( ctype = 'IT' zone = 'B' price = '0.39' ) + ( ctype = 'IT' zone = 'C' price = '0.59' ) + ( ctype = 'MO' zone = 'A' price = '0.29' ) + ( ctype = 'MO' zone = 'B' price = '0.49' ) + ( ctype = 'MO' zone = 'C' price = '0.69' ) + ( ctype = 'SP' zone = 'A' price = '0.39' ) + ( ctype = 'SP' zone = 'B' price = '0.59' ) + ( ctype = 'SP' zone = 'C' price = '0.79' ) ). + + READ TABLE lt_prices INTO ls_price + WITH TABLE KEY ctype = iv_ctype zone = iv_zone. + IF sy-subrc = 0 AND ls_price-price > 0. + ev_charge = round( val = ev_minutes * ls_price-price + 0.025 + dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1109/reference/tel_test.clas.abap b/tasks_gen/train/G1109/reference/tel_test.clas.abap new file mode 100644 index 0000000..b70114b --- /dev/null +++ b/tasks_gen/train/G1109/reference/tel_test.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}tel_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS rate + IMPORTING iv_ctype TYPE string + iv_zone TYPE string + iv_secs TYPE i + EXPORTING ev_minutes TYPE i + ev_charge TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS zero_duration FOR TESTING. + METHODS national_call FOR TESTING. + METHODS rounds_up FOR TESTING. + METHODS unknown_type FOR TESTING. + METHODS negative_secs FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tel_test IMPLEMENTATION. + METHOD rate. + CALL FUNCTION '{{P}}TEL_RATE' + EXPORTING + iv_ctype = iv_ctype + iv_zone = iv_zone + iv_secs = iv_secs + IMPORTING + ev_minutes = ev_minutes + ev_charge = ev_charge + EXCEPTIONS + invalid_duration = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD zero_duration. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = 0 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD national_call. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = 30 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.12' ) act = lv_charge ). + ENDMETHOD. + + METHOD rounds_up. + rate( EXPORTING iv_ctype = 'IT' iv_zone = 'B' iv_secs = 61 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.81' ) act = lv_charge ). + ENDMETHOD. + + METHOD unknown_type. + rate( EXPORTING iv_ctype = 'XX' iv_zone = 'A' iv_secs = 60 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_charge ). + ENDMETHOD. + + METHOD negative_secs. + rate( EXPORTING iv_ctype = 'NT' iv_zone = 'A' iv_secs = -1 + IMPORTING ev_minutes = DATA(lv_min) ev_charge = DATA(lv_charge) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1109/seed/tel_sample.func.abap b/tasks_gen/train/G1109/seed/tel_sample.func.abap new file mode 100644 index 0000000..f4e115b --- /dev/null +++ b/tasks_gen/train/G1109/seed/tel_sample.func.abap @@ -0,0 +1,16 @@ +FUNCTION {{p}}tel_sample + IMPORTING + iv_ctype TYPE string + iv_zone TYPE string + iv_secs TYPE i + EXPORTING + ev_minutes TYPE i + ev_charge TYPE decfloat34 + EXCEPTIONS + invalid_duration. + + " Sample module: it shows the interface of all rating modules. + ev_minutes = 0. + ev_charge = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1109/spec.md b/tasks_gen/train/G1109/spec.md new file mode 100644 index 0000000..c794cf3 --- /dev/null +++ b/tasks_gen/train/G1109/spec.md @@ -0,0 +1,63 @@ +# 1. Goal + +A telephone company rates the calls of its customers. The rating program +collects the data of one call and calls one function module for this call. The +function module must have the same interface as the sample function module, +because the rating program calls all rating modules in the same way. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_TEL exists in package $TMP. +- Function module {{P}}TEL_SAMPLE exists in this group. It is the sample rating + module. Its interface is the interface that all rating modules must use. +- A call has a call type, a destination zone and a duration in seconds. + +# 4. Contract + +- Create the function module {{P}}TEL_RATE in function group {{P}}FG_TEL. +- Use exactly the interface of {{P}}TEL_SAMPLE: + - importing parameter iv_ctype, type string, + - importing parameter iv_zone, type string, + - importing parameter iv_secs, type i, + - exporting parameter ev_minutes, type i, + - exporting parameter ev_charge, type decfloat34, + - exception invalid_duration. +- iv_ctype is the call type of the call. iv_zone is the destination zone of the + call. iv_secs is the duration of the call in seconds. +- ev_minutes is the billed duration of the call in minutes. +- ev_charge is the charge of the call. + +# 5. Business rules + +1. If iv_secs is less than 0, raise the exception INVALID_DURATION. ev_minutes + and ev_charge stay 0. +2. ev_minutes is iv_secs rounded up to the next full minute. 1 to 60 seconds is + 1 minute. 61 to 120 seconds is 2 minutes. 121 to 180 seconds is 3 minutes, + and so on. If iv_secs is 0, ev_minutes is 0. +3. The price of one billed minute depends on the call type and on the zone: + - call type NT: zone A 0.09, zone B 0.19, zone C 0.29 + - call type IT: zone A 0.19, zone B 0.39, zone C 0.59 + - call type MO: zone A 0.29, zone B 0.49, zone C 0.69 + - call type SP: zone A 0.39, zone B 0.59, zone C 0.79 +4. If the call type is not NT, IT, MO or SP, or the zone is not A, B or C, the + price of one minute is 0. +5. If ev_minutes is 0 or the price of one minute is 0, ev_charge is 0. +6. Otherwise ev_charge is ev_minutes multiplied by the price of one minute plus + a connection fee of 0.025. Round ev_charge half up to 2 decimals. + +# 6. Constraints + +- Release target: 8.16. +- Package: $TMP. Do not use a transport. +- Out of scope: do not change {{P}}TEL_SAMPLE. +- Coding standards: Clean ABAP. A method has fewer than 40 statements. + +# 7. Acceptance + +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1109/task.json b/tasks_gen/train/G1109/task.json new file mode 100644 index 0000000..020cefd --- /dev/null +++ b/tasks_gen/train/G1109/task.json @@ -0,0 +1,86 @@ +{ + "id": "G1109", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_TEL", + "description": "Call rating modules" + }, + { + "type": "FUNC", + "name": "{{P}}TEL_SAMPLE", + "functionGroup": "{{P}}FG_TEL", + "file": "seed/tel_sample.func.abap", + "description": "Sample rating module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}TEL_RATE", + "functionGroup": "{{P}}FG_TEL", + "params": [ + { + "name": "iv_ctype", + "type": "string" + }, + { + "name": "iv_zone", + "type": "string" + }, + { + "name": "iv_secs", + "type": "i" + }, + { + "name": "ev_minutes", + "type": "i" + }, + { + "name": "ev_charge", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}TEL_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TEL_HIDDEN", + "file": "hidden/tel_hidden.clas.abap", + "description": "Hidden tests for the call rating" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}TEL_RATE", + "functionGroup": "{{P}}FG_TEL", + "file": "reference/tel_rate.func.abap", + "description": "Call rating module" + }, + { + "type": "CLAS", + "name": "{{P}}TEL_TEST", + "file": "reference/tel_test.clas.abap", + "description": "Own tests for the call rating" + } + ], + "craft_checks": [ + "method_length", + "sorted_table_with_key", + "read_table_with_key" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1110/faulty/m1_scrap_pay.prog.abap b/tasks_gen/train/G1110/faulty/m1_scrap_pay.prog.abap new file mode 100644 index 0000000..d262c5e --- /dev/null +++ b/tasks_gen/train/G1110/faulty/m1_scrap_pay.prog.abap @@ -0,0 +1,115 @@ +REPORT {{p}}scrap_pay. + +TYPES: BEGIN OF ty_line, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_metal TYPE RANGE OF {{p}}scrap-metal. + +DATA gv_metal TYPE {{p}}scrap-metal. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10'. + CONSTANTS c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + + METHODS list + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD list. + SELECT metal, + SUM( weight_kg ) AS total_kg, + SUM( weight_kg * price_kg ) AS total_pay, + COUNT( * ) AS deliv_cnt + FROM {{p}}scrap + WHERE deliv_date <> @iv_date + AND metal IN @it_metal + GROUP BY metal + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -total_kg >= c_limit. + -total_pay = -total_pay * c_bonus. + ENDIF. + ENDLOOP. + + SORT rt_lines BY total_pay DESCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->list( iv_date = p_date it_metal = s_metal[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS bonus_above_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SCRAP' ) ) ). + DATA lt_scrap TYPE STANDARD TABLE OF {{p}}scrap WITH EMPTY KEY. + lt_scrap = VALUE #( ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '600.000' price_kg = '2.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '100.000' price_kg = '2.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '10.000' price_kg = '1.00' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260116' weight_kg = '10.000' price_kg = '1.00' ) ). + go_env->insert_test_data( lt_scrap ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD bonus_above_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + lv_expected = '1540.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-total_pay ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + lv_expected = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 2 ]-total_pay ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lt_lines) = NEW lcl_report( )->list( + iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'AL' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AL' act = lt_lines[ 1 ]-metal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/faulty/m2_scrap_pay.prog.abap b/tasks_gen/train/G1110/faulty/m2_scrap_pay.prog.abap new file mode 100644 index 0000000..1acab6a --- /dev/null +++ b/tasks_gen/train/G1110/faulty/m2_scrap_pay.prog.abap @@ -0,0 +1,115 @@ +REPORT {{p}}scrap_pay. + +TYPES: BEGIN OF ty_line, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_metal TYPE RANGE OF {{p}}scrap-metal. + +DATA gv_metal TYPE {{p}}scrap-metal. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10'. + CONSTANTS c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + + METHODS list + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD list. + SELECT metal, + SUM( weight_kg ) AS total_kg, + SUM( weight_kg * price_kg ) AS total_pay, + COUNT( * ) AS deliv_cnt + FROM {{p}}scrap + WHERE deliv_date = @iv_date + OR metal IN @it_metal + GROUP BY metal + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -total_kg >= c_limit. + -total_pay = -total_pay * c_bonus. + ENDIF. + ENDLOOP. + + SORT rt_lines BY total_pay DESCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->list( iv_date = p_date it_metal = s_metal[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS bonus_above_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SCRAP' ) ) ). + DATA lt_scrap TYPE STANDARD TABLE OF {{p}}scrap WITH EMPTY KEY. + lt_scrap = VALUE #( ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '600.000' price_kg = '2.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '100.000' price_kg = '2.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '10.000' price_kg = '1.00' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260116' weight_kg = '10.000' price_kg = '1.00' ) ). + go_env->insert_test_data( lt_scrap ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD bonus_above_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + lv_expected = '1540.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-total_pay ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + lv_expected = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 2 ]-total_pay ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lt_lines) = NEW lcl_report( )->list( + iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'AL' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AL' act = lt_lines[ 1 ]-metal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/faulty/m3_scrap_pay.prog.abap b/tasks_gen/train/G1110/faulty/m3_scrap_pay.prog.abap new file mode 100644 index 0000000..e45b364 --- /dev/null +++ b/tasks_gen/train/G1110/faulty/m3_scrap_pay.prog.abap @@ -0,0 +1,115 @@ +REPORT {{p}}scrap_pay. + +TYPES: BEGIN OF ty_line, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_metal TYPE RANGE OF {{p}}scrap-metal. + +DATA gv_metal TYPE {{p}}scrap-metal. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10'. + CONSTANTS c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + + METHODS list + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD list. + SELECT metal, + SUM( weight_kg ) AS total_kg, + SUM( weight_kg * price_kg ) AS total_pay, + COUNT( * ) AS deliv_cnt + FROM {{p}}scrap + WHERE deliv_date = @iv_date + AND metal IN @it_metal + GROUP BY metal + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -total_kg < c_limit. + -total_pay = -total_pay * c_bonus. + ENDIF. + ENDLOOP. + + SORT rt_lines BY total_pay DESCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->list( iv_date = p_date it_metal = s_metal[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS bonus_above_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SCRAP' ) ) ). + DATA lt_scrap TYPE STANDARD TABLE OF {{p}}scrap WITH EMPTY KEY. + lt_scrap = VALUE #( ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '600.000' price_kg = '2.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '100.000' price_kg = '2.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '10.000' price_kg = '1.00' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260116' weight_kg = '10.000' price_kg = '1.00' ) ). + go_env->insert_test_data( lt_scrap ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD bonus_above_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + lv_expected = '1540.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-total_pay ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + lv_expected = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 2 ]-total_pay ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lt_lines) = NEW lcl_report( )->list( + iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'AL' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AL' act = lt_lines[ 1 ]-metal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/faulty/m4_scrap_pay.prog.abap b/tasks_gen/train/G1110/faulty/m4_scrap_pay.prog.abap new file mode 100644 index 0000000..14aaeb5 --- /dev/null +++ b/tasks_gen/train/G1110/faulty/m4_scrap_pay.prog.abap @@ -0,0 +1,115 @@ +REPORT {{p}}scrap_pay. + +TYPES: BEGIN OF ty_line, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_metal TYPE RANGE OF {{p}}scrap-metal. + +DATA gv_metal TYPE {{p}}scrap-metal. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10'. + CONSTANTS c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + + METHODS list + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD list. + SELECT metal, + SUM( weight_kg ) AS total_kg, + SUM( weight_kg * price_kg ) AS total_pay, + COUNT( * ) AS deliv_cnt + FROM {{p}}scrap + WHERE deliv_date = @iv_date + AND metal IN @it_metal + GROUP BY metal + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -total_kg >= c_limit. + -total_pay = -total_pay / c_bonus. + ENDIF. + ENDLOOP. + + SORT rt_lines BY total_pay DESCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->list( iv_date = p_date it_metal = s_metal[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS bonus_above_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SCRAP' ) ) ). + DATA lt_scrap TYPE STANDARD TABLE OF {{p}}scrap WITH EMPTY KEY. + lt_scrap = VALUE #( ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '600.000' price_kg = '2.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '100.000' price_kg = '2.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '10.000' price_kg = '1.00' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260116' weight_kg = '10.000' price_kg = '1.00' ) ). + go_env->insert_test_data( lt_scrap ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD bonus_above_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + lv_expected = '1540.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-total_pay ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + lv_expected = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 2 ]-total_pay ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lt_lines) = NEW lcl_report( )->list( + iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'AL' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AL' act = lt_lines[ 1 ]-metal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/generation.json b/tasks_gen/train/G1110/generation.json new file mode 100644 index 0000000..54f5bcc --- /dev/null +++ b/tasks_gen/train/G1110/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1110", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1110/hidden/scrap_hidden.clas.abap b/tasks_gen/train/G1110/hidden/scrap_hidden.clas.abap new file mode 100644 index 0000000..b566278 --- /dev/null +++ b/tasks_gen/train/G1110/hidden/scrap_hidden.clas.abap @@ -0,0 +1,117 @@ +CLASS {{p}}scrap_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_metal TYPE RANGE OF {{p}}scrap-metal. + + METHODS run_report + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + + METHODS all_metals FOR TESTING. + METHODS sorted_by_pay FOR TESTING. + METHODS bonus_over_limit FOR TESTING. + METHODS bonus_at_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS counts FOR TESTING. + METHODS empty_result FOR TESTING. +ENDCLASS. + + +CLASS {{p}}scrap_hidden IMPLEMENTATION. + METHOD run_report. + cl_salv_bs_runtime_info=>set( display = abap_false + metadata = abap_false + data = abap_true ). + SUBMIT {{p}}scrap_pay WITH p_date = iv_date WITH s_metal IN it_metal AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS NOT BOUND. + cl_abap_unit_assert=>fail( msg = 'No ALV data' ). + ENDIF. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } is missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_metals. + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( run_report( '20260115' ) ) ). + ENDMETHOD. + + METHOD sorted_by_pay. + DATA(lr_data) = run_report( '20260115' ). + cl_abap_unit_assert=>assert_equals( exp = `CU` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `METAL` ) ). + cl_abap_unit_assert=>assert_equals( exp = `BR` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `METAL` ) ). + ENDMETHOD. + + METHOD bonus_over_limit. + DATA(lr_data) = run_report( '20260115' ). + cl_abap_unit_assert=>assert_equals( exp = `3630.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL_PAY` ) ). + cl_abap_unit_assert=>assert_equals( exp = `550.000` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL_KG` ) ). + ENDMETHOD. + + METHOD bonus_at_limit. + DATA(lr_data) = run_report( '20260115' ). + cl_abap_unit_assert=>assert_equals( exp = `759.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TOTAL_PAY` ) ). + cl_abap_unit_assert=>assert_equals( exp = `500.000` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TOTAL_KG` ) ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA(lr_data) = run_report( '20260115' ). + cl_abap_unit_assert=>assert_equals( exp = `80.00` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `TOTAL_PAY` ) ). + cl_abap_unit_assert=>assert_equals( exp = `60.00` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `TOTAL_PAY` ) ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lr_data) = run_report( iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'CU' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `3630.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL_PAY` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run_report( '20260110' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `3300.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL_PAY` ) ). + cl_abap_unit_assert=>assert_equals( exp = `30.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TOTAL_PAY` ) ). + ENDMETHOD. + + METHOD counts. + DATA(lr_data) = run_report( '20260115' ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DELIV_CNT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DELIV_CNT` ) ). + ENDMETHOD. + + METHOD empty_result. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( run_report( '20261231' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/mutation.json b/tasks_gen/train/G1110/mutation.json new file mode 100644 index 0000000..ab754c5 --- /dev/null +++ b/tasks_gen/train/G1110/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1110", + "mutants": [ + { + "object": "{{P}}SCRAP_PAY", + "mutant": "line 35: * -> / (arith)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}SCRAP_PAY", + "mutant": "line 38: = -> <> (eq)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "ALL_METALS", + "BONUS_AT_LIMIT", + "BONUS_OVER_LIMIT", + "COUNTS", + "DATE_FILTER", + "EMPTY_RESULT", + "METAL_FILTER", + "NO_BONUS_BELOW_LIMIT", + "SORTED_BY_PAY" + ] + }, + { + "object": "{{P}}SCRAP_PAY", + "mutant": "line 39: AND -> OR (logic)", + "status": "killed", + "hidden": "2/9", + "failed_tests": [ + "BONUS_OVER_LIMIT", + "COUNTS", + "DATE_FILTER", + "EMPTY_RESULT", + "METAL_FILTER", + "NO_BONUS_BELOW_LIMIT", + "SORTED_BY_PAY" + ] + }, + { + "object": "{{P}}SCRAP_PAY", + "mutant": "line 44: >= -> < (rel)", + "status": "killed", + "hidden": "4/9", + "failed_tests": [ + "BONUS_AT_LIMIT", + "BONUS_OVER_LIMIT", + "DATE_FILTER", + "METAL_FILTER", + "NO_BONUS_BELOW_LIMIT" + ] + }, + { + "object": "{{P}}SCRAP_PAY", + "mutant": "line 45: * -> / (arith)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "BONUS_AT_LIMIT", + "BONUS_OVER_LIMIT", + "DATE_FILTER", + "METAL_FILTER" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1110/reference/scrap_pay.prog.abap b/tasks_gen/train/G1110/reference/scrap_pay.prog.abap new file mode 100644 index 0000000..599511f --- /dev/null +++ b/tasks_gen/train/G1110/reference/scrap_pay.prog.abap @@ -0,0 +1,115 @@ +REPORT {{p}}scrap_pay. + +TYPES: BEGIN OF ty_line, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_metal TYPE RANGE OF {{p}}scrap-metal. + +DATA gv_metal TYPE {{p}}scrap-metal. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10'. + CONSTANTS c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + + METHODS list + IMPORTING iv_date TYPE d + it_metal TYPE tt_metal + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD list. + SELECT metal, + SUM( weight_kg ) AS total_kg, + SUM( weight_kg * price_kg ) AS total_pay, + COUNT( * ) AS deliv_cnt + FROM {{p}}scrap + WHERE deliv_date = @iv_date + AND metal IN @it_metal + GROUP BY metal + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -total_kg >= c_limit. + -total_pay = -total_pay * c_bonus. + ENDIF. + ENDLOOP. + + SORT rt_lines BY total_pay DESCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->list( iv_date = p_date it_metal = s_metal[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS bonus_above_limit FOR TESTING. + METHODS no_bonus_below_limit FOR TESTING. + METHODS metal_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SCRAP' ) ) ). + DATA lt_scrap TYPE STANDARD TABLE OF {{p}}scrap WITH EMPTY KEY. + lt_scrap = VALUE #( ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '600.000' price_kg = '2.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '100.000' price_kg = '2.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '10.000' price_kg = '1.00' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260116' weight_kg = '10.000' price_kg = '1.00' ) ). + go_env->insert_test_data( lt_scrap ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD bonus_above_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + lv_expected = '1540.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-total_pay ). + ENDMETHOD. + + METHOD no_bonus_below_limit. + DATA lv_expected TYPE p LENGTH 15 DECIMALS 2. + DATA(lt_lines) = NEW lcl_report( )->list( iv_date = '20260115' it_metal = VALUE #( ) ). + lv_expected = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 2 ]-total_pay ). + ENDMETHOD. + + METHOD metal_filter. + DATA(lt_lines) = NEW lcl_report( )->list( + iv_date = '20260115' + it_metal = VALUE #( ( sign = 'I' option = 'EQ' low = 'AL' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AL' act = lt_lines[ 1 ]-metal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/seed/scrap.tabl.asabap b/tasks_gen/train/G1110/seed/scrap.tabl.asabap new file mode 100644 index 0000000..19fd607 --- /dev/null +++ b/tasks_gen/train/G1110/seed/scrap.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Scrap metal deliveries' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}scrap { + key client : abap.clnt not null; + key deliv_id : abap.int4 not null; + metal : abap.char(4); + deliv_date : abap.dats; + weight_kg : abap.dec(13,3); + price_kg : abap.dec(13,2); +} diff --git a/tasks_gen/train/G1110/seed/scrap_pay.prog.abap b/tasks_gen/train/G1110/seed/scrap_pay.prog.abap new file mode 100644 index 0000000..1945e04 --- /dev/null +++ b/tasks_gen/train/G1110/seed/scrap_pay.prog.abap @@ -0,0 +1,105 @@ +REPORT {{p}}scrap_pay. + +*--------------------------------------------------------------------* +* Payout list of the recycling yard. +* Legacy program. The output columns and the values must not change. +*--------------------------------------------------------------------* + +TYPES: BEGIN OF ty_out, + metal TYPE {{p}}scrap-metal, + total_kg TYPE p LENGTH 13 DECIMALS 3, + total_pay TYPE p LENGTH 15 DECIMALS 2, + deliv_cnt TYPE i, + END OF ty_out. + +DATA: gv_metal TYPE {{p}}scrap-metal, + gt_scrap TYPE STANDARD TABLE OF {{p}}scrap, + gt_out TYPE STANDARD TABLE OF ty_out, + gs_out TYPE ty_out. + +CONSTANTS: c_bonus TYPE p LENGTH 5 DECIMALS 2 VALUE '1.10', + c_limit TYPE p LENGTH 13 DECIMALS 3 VALUE '500.000'. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_metal FOR gv_metal. + +START-OF-SELECTION. + PERFORM read_data. + PERFORM build_totals. + PERFORM add_bonus. + PERFORM sort_list. + PERFORM show_list. + +*&---------------------------------------------------------------------* +*& Form read_data +*&---------------------------------------------------------------------* +FORM read_data. + CLEAR gt_scrap. + SELECT * FROM {{p}}scrap INTO TABLE gt_scrap + WHERE deliv_date = p_date + AND metal IN s_metal. +ENDFORM. + +*&---------------------------------------------------------------------* +*& Form build_totals +*&---------------------------------------------------------------------* +FORM build_totals. + DATA: ls_scrap TYPE {{p}}scrap, + lv_pay TYPE p LENGTH 15 DECIMALS 2, + lv_index TYPE sy-tabix. + + CLEAR gt_out. + LOOP AT gt_scrap INTO ls_scrap. + CLEAR lv_index. + READ TABLE gt_out INTO gs_out WITH KEY metal = ls_scrap-metal. + IF sy-subrc = 0. + lv_index = sy-tabix. + ELSE. + CLEAR gs_out. + gs_out-metal = ls_scrap-metal. + ENDIF. + lv_pay = ls_scrap-weight_kg * ls_scrap-price_kg. + gs_out-total_kg = gs_out-total_kg + ls_scrap-weight_kg. + gs_out-total_pay = gs_out-total_pay + lv_pay. + gs_out-deliv_cnt = gs_out-deliv_cnt + 1. + IF lv_index IS INITIAL. + APPEND gs_out TO gt_out. + ELSE. + MODIFY gt_out FROM gs_out INDEX lv_index. + ENDIF. + ENDLOOP. +ENDFORM. + +*&---------------------------------------------------------------------* +*& Form add_bonus +*&---------------------------------------------------------------------* +FORM add_bonus. + LOOP AT gt_out INTO gs_out. + IF gs_out-total_kg >= c_limit. + gs_out-total_pay = gs_out-total_pay * c_bonus. + MODIFY gt_out FROM gs_out. + ENDIF. + ENDLOOP. +ENDFORM. + +*&---------------------------------------------------------------------* +*& Form sort_list +*&---------------------------------------------------------------------* +FORM sort_list. + SORT gt_out BY total_pay DESCENDING. +ENDFORM. + +*&---------------------------------------------------------------------* +*& Form show_list +*&---------------------------------------------------------------------* +FORM show_list. + DATA lo_alv TYPE REF TO cl_salv_table. + + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv + CHANGING t_table = gt_out ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. +ENDFORM. diff --git a/tasks_gen/train/G1110/seed/scrap_seed.clas.abap b/tasks_gen/train/G1110/seed/scrap_seed.clas.abap new file mode 100644 index 0000000..fcfe5d4 --- /dev/null +++ b/tasks_gen/train/G1110/seed/scrap_seed.clas.abap @@ -0,0 +1,21 @@ +CLASS {{p}}scrap_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}scrap_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}scrap. + INSERT {{p}}scrap FROM TABLE @( VALUE #( + ( deliv_id = 1 metal = 'CU' deliv_date = '20260115' weight_kg = '300.000' price_kg = '6.00' ) + ( deliv_id = 2 metal = 'CU' deliv_date = '20260115' weight_kg = '250.000' price_kg = '6.00' ) + ( deliv_id = 3 metal = 'AL' deliv_date = '20260115' weight_kg = '100.000' price_kg = '1.50' ) + ( deliv_id = 4 metal = 'AL' deliv_date = '20260115' weight_kg = '200.000' price_kg = '1.20' ) + ( deliv_id = 5 metal = 'AL' deliv_date = '20260115' weight_kg = '200.000' price_kg = '1.50' ) + ( deliv_id = 6 metal = 'FE' deliv_date = '20260115' weight_kg = '400.000' price_kg = '0.20' ) + ( deliv_id = 7 metal = 'BR' deliv_date = '20260115' weight_kg = '20.000' price_kg = '3.00' ) + ( deliv_id = 8 metal = 'CU' deliv_date = '20260110' weight_kg = '500.000' price_kg = '6.00' ) + ( deliv_id = 9 metal = 'BR' deliv_date = '20260110' weight_kg = '10.000' price_kg = '3.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1110/spec.md b/tasks_gen/train/G1110/spec.md new file mode 100644 index 0000000..fe03d4c --- /dev/null +++ b/tasks_gen/train/G1110/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +The recycling yard pays its customers for scrap metal deliveries. The report +{{P}}SCRAP_PAY shows the payout of each metal type for one delivery day in an +ALV list. The yard uses the list to check the daily payouts. + +The report is a legacy program. It works, but its code is hard to read and hard +to maintain. Refactor the report. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}SCRAP exists in package $TMP and contains data. + Fields: DELIV_ID (INT4, key), METAL (CHAR 4), DELIV_DATE (DATS), + WEIGHT_KG (DEC 13,3), PRICE_KG (DEC 13,2). +- The executable program {{P}}SCRAP_PAY exists in package $TMP and is active. + It has a selection screen and shows an ALV list. +- The program {{P}}SCRAP_PAY contains the business rules of the payout list. + Read the code of the program before you change it. + +# 4. Contract +- Refactor the executable program {{P}}SCRAP_PAY in package $TMP. The name stays + the same. +- The selection screen stays the same: + - Parameter P_DATE, type DATS, mandatory: the delivery day. + - Select-option S_METAL for the metal type. +- The ALV list stays the same. The output table has these columns with these + names and these value formats: + - METAL: the metal type. + - TOTAL_KG: the total weight of the metal in kg, 3 decimals. + - TOTAL_PAY: the total payout for the metal, 2 decimals. + - DELIV_CNT: the number of deliveries of the metal. + +# 5. Business rules +- The behavior of the program stays the same. The code of the legacy program + {{P}}SCRAP_PAY defines every rule: which deliveries are included, how the + totals are calculated, and in which order the lines are shown. +- The rules of the legacy program are correct. Do not correct, add or remove a + rule. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Small methods, clear names, no unused code, no + FORM routine. Read the data of the list with one database access. + No ATC priority 1 or 2 finding. +- Out of scope: do not change the table {{P}}SCRAP. Do not change the selection + screen, the ALV column names or the values of the ALV list. + +# 7. Acceptance +- The program {{P}}SCRAP_PAY is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1110/task.json b/tasks_gen/train/G1110/task.json new file mode 100644 index 0000000..9f807c6 --- /dev/null +++ b/tasks_gen/train/G1110/task.json @@ -0,0 +1,66 @@ +{ + "id": "G1110", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SCRAP", + "file": "seed/scrap.tabl.asabap", + "description": "Scrap metal deliveries of the recycling yard" + }, + { + "type": "PROG", + "name": "{{P}}SCRAP_PAY", + "file": "seed/scrap_pay.prog.abap", + "description": "Legacy payout report (contract object, refactor in place)" + }, + { + "type": "CLAS", + "name": "{{P}}SCRAP_SEED", + "file": "seed/scrap_seed.clas.abap", + "description": "Seed data for {{P}}SCRAP", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}SCRAP_PAY", + "parameters": [ + "p_date", + "s_metal" + ] + } + ], + "out_of_scope": [ + "{{P}}SCRAP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SCRAP_HIDDEN", + "file": "hidden/scrap_hidden.clas.abap", + "description": "Hidden tests for the payout report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}SCRAP_PAY", + "file": "reference/scrap_pay.prog.abap", + "description": "Refactored payout report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1111/faulty/m0_i_tour_load.ddls.asddls b/tasks_gen/train/G1111/faulty/m0_i_tour_load.ddls.asddls new file mode 100644 index 0000000..4206702 --- /dev/null +++ b/tasks_gen/train/G1111/faulty/m0_i_tour_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tour load' +define view entity {{P}}I_TOUR_LOAD + as select from {{p}}tour as tour + left outer join {{p}}parcel as parcel on parcel.tour_id = tour.tour_id +{ + key tour.tour_id as tour_id, + tour.driver as driver, + tour.depot as depot, + count( * ) as parcel_count, + sum( parcel.weight ) as total_weight +} +group by + tour.tour_id, + tour.driver, + tour.depot diff --git a/tasks_gen/train/G1111/faulty/m1_i_tour_load.ddls.asddls b/tasks_gen/train/G1111/faulty/m1_i_tour_load.ddls.asddls new file mode 100644 index 0000000..6ee4b8c --- /dev/null +++ b/tasks_gen/train/G1111/faulty/m1_i_tour_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tour load' +define view entity {{P}}I_TOUR_LOAD + as select from {{p}}tour as tour + inner join {{p}}parcel as parcel on parcel.tour_id <> tour.tour_id +{ + key tour.tour_id as tour_id, + tour.driver as driver, + tour.depot as depot, + count( * ) as parcel_count, + sum( parcel.weight ) as total_weight +} +group by + tour.tour_id, + tour.driver, + tour.depot diff --git a/tasks_gen/train/G1111/faulty/m2_i_tour_load.ddls.asddls b/tasks_gen/train/G1111/faulty/m2_i_tour_load.ddls.asddls new file mode 100644 index 0000000..8a9719d --- /dev/null +++ b/tasks_gen/train/G1111/faulty/m2_i_tour_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tour load' +define view entity {{P}}I_TOUR_LOAD + as select from {{p}}tour as tour + inner join {{p}}parcel as parcel on parcel.tour_id = tour.tour_id +{ + key tour.tour_id as tour_id, + tour.driver as driver, + tour.depot as depot, + count( * ) as parcel_count, + max( parcel.weight ) as total_weight +} +group by + tour.tour_id, + tour.driver, + tour.depot diff --git a/tasks_gen/train/G1111/generation.json b/tasks_gen/train/G1111/generation.json new file mode 100644 index 0000000..8266fe9 --- /dev/null +++ b/tasks_gen/train/G1111/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1111", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1111/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1111/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..2e7d91e --- /dev/null +++ b/tasks_gen/train/G1111/hidden/t31_hidden.clas.abap @@ -0,0 +1,98 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS one_row_per_tour FOR TESTING. + METHODS counts_all_parcels FOR TESTING. + METHODS sums_all_weights FOR TESTING. + METHODS tour_without_parcels FOR TESTING. + METHODS driver_and_depot FOR TESTING. + METHODS single_parcel_tour FOR TESTING. + METHODS many_zones_one_row FOR TESTING. + METHODS empty_tables FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TOUR_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_tour TYPE STANDARD TABLE OF {{p}}tour WITH EMPTY KEY. + DATA lt_parcel TYPE STANDARD TABLE OF {{p}}parcel WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_tour = VALUE #( ( tour_id = 'T1' driver = 'D1' depot = 'N01' ) + ( tour_id = 'T2' driver = 'D2' depot = 'N01' ) + ( tour_id = 'T3' driver = 'D3' depot = 'S02' ) + ( tour_id = 'T4' driver = 'D4' depot = 'S02' ) + ( tour_id = 'T5' driver = 'D5' depot = 'N01' ) ). + lt_parcel = VALUE #( ( tour_id = 'T1' parcel_no = 1 dest_zone = 'A' weight = '1.500' ) + ( tour_id = 'T1' parcel_no = 2 dest_zone = 'B' weight = '2.250' ) + ( tour_id = 'T1' parcel_no = 3 dest_zone = 'A' weight = '0.250' ) + ( tour_id = 'T2' parcel_no = 1 dest_zone = 'A' weight = '10.000' ) + ( tour_id = 'T4' parcel_no = 1 dest_zone = 'C' weight = '4.125' ) + ( tour_id = 'T5' parcel_no = 1 dest_zone = 'A' weight = '1.000' ) + ( tour_id = 'T5' parcel_no = 2 dest_zone = 'B' weight = '1.000' ) + ( tour_id = 'T5' parcel_no = 3 dest_zone = 'C' weight = '1.000' ) + ( tour_id = 'T5' parcel_no = 4 dest_zone = 'D' weight = '1.000' ) ). + go_env->insert_test_data( lt_tour ). + go_env->insert_test_data( lt_parcel ). + ENDMETHOD. + + METHOD one_row_per_tour. + SELECT COUNT(*) FROM {{p}}i_tour_load INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rows ). + ENDMETHOD. + + METHOD counts_all_parcels. + SELECT SINGLE parcel_count FROM {{p}}i_tour_load WHERE tour_id = 'T1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD sums_all_weights. + SELECT SINGLE total_weight FROM {{p}}i_tour_load WHERE tour_id = 'T1' INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.000' ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD tour_without_parcels. + SELECT COUNT(*) FROM {{p}}i_tour_load WHERE tour_id = 'T3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD driver_and_depot. + SELECT SINGLE driver, depot FROM {{p}}i_tour_load WHERE tour_id = 'T2' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 'D2' act = ls_row-driver ). + cl_abap_unit_assert=>assert_equals( exp = 'N01' act = ls_row-depot ). + ENDMETHOD. + + METHOD single_parcel_tour. + SELECT SINGLE parcel_count, total_weight FROM {{p}}i_tour_load WHERE tour_id = 'T4' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-parcel_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.125' ) + act = CONV decfloat34( ls_row-total_weight ) ). + ENDMETHOD. + + METHOD many_zones_one_row. + SELECT SINGLE parcel_count, total_weight FROM {{p}}i_tour_load WHERE tour_id = 'T5' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-parcel_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.000' ) + act = CONV decfloat34( ls_row-total_weight ) ). + ENDMETHOD. + + METHOD empty_tables. + go_env->clear_doubles( ). + SELECT COUNT(*) FROM {{p}}i_tour_load INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1111/mutation.json b/tasks_gen/train/G1111/mutation.json new file mode 100644 index 0000000..e7ce28e --- /dev/null +++ b/tasks_gen/train/G1111/mutation.json @@ -0,0 +1,43 @@ +{ + "task": "G1111", + "mutants": [ + { + "object": "{{P}}I_TOUR_LOAD", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "ONE_ROW_PER_TOUR", + "TOUR_WITHOUT_PARCELS" + ] + }, + { + "object": "{{P}}I_TOUR_LOAD", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "COUNTS_ALL_PARCELS", + "MANY_ZONES_ONE_ROW", + "ONE_ROW_PER_TOUR", + "SINGLE_PARCEL_TOUR", + "SUMS_ALL_WEIGHTS", + "TOUR_WITHOUT_PARCELS" + ] + }, + { + "object": "{{P}}I_TOUR_LOAD", + "mutant": "line 11: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "MANY_ZONES_ONE_ROW", + "SUMS_ALL_WEIGHTS" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1111/reference/i_tour_load.ddls.asddls b/tasks_gen/train/G1111/reference/i_tour_load.ddls.asddls new file mode 100644 index 0000000..cd7605c --- /dev/null +++ b/tasks_gen/train/G1111/reference/i_tour_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tour load' +define view entity {{P}}I_TOUR_LOAD + as select from {{p}}tour as tour + inner join {{p}}parcel as parcel on parcel.tour_id = tour.tour_id +{ + key tour.tour_id as tour_id, + tour.driver as driver, + tour.depot as depot, + count( * ) as parcel_count, + sum( parcel.weight ) as total_weight +} +group by + tour.tour_id, + tour.driver, + tour.depot diff --git a/tasks_gen/train/G1111/reference/t31_test.clas.abap b/tasks_gen/train/G1111/reference/t31_test.clas.abap new file mode 100644 index 0000000..c5df969 --- /dev/null +++ b/tasks_gen/train/G1111/reference/t31_test.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS one_row_for_multi_zone FOR TESTING. + METHODS total_of_two_parcels FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TOUR_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD one_row_for_multi_zone. + DATA lt_tour TYPE STANDARD TABLE OF {{p}}tour WITH EMPTY KEY. + DATA lt_parcel TYPE STANDARD TABLE OF {{p}}parcel WITH EMPTY KEY. + lt_tour = VALUE #( ( tour_id = 'A' driver = 'DA' depot = 'N01' ) ). + lt_parcel = VALUE #( ( tour_id = 'A' parcel_no = 1 dest_zone = 'A' weight = '1.000' ) + ( tour_id = 'A' parcel_no = 2 dest_zone = 'B' weight = '2.000' ) ). + go_env->insert_test_data( lt_tour ). + go_env->insert_test_data( lt_parcel ). + SELECT COUNT(*) FROM {{p}}i_tour_load INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD total_of_two_parcels. + DATA lt_tour TYPE STANDARD TABLE OF {{p}}tour WITH EMPTY KEY. + DATA lt_parcel TYPE STANDARD TABLE OF {{p}}parcel WITH EMPTY KEY. + lt_tour = VALUE #( ( tour_id = 'B' driver = 'DB' depot = 'S02' ) ). + lt_parcel = VALUE #( ( tour_id = 'B' parcel_no = 1 dest_zone = 'A' weight = '1.500' ) + ( tour_id = 'B' parcel_no = 2 dest_zone = 'A' weight = '2.500' ) ). + go_env->insert_test_data( lt_tour ). + go_env->insert_test_data( lt_parcel ). + SELECT SINGLE parcel_count, total_weight FROM {{p}}i_tour_load WHERE tour_id = 'B' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-parcel_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.000' ) + act = CONV decfloat34( ls_row-total_weight ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1111/seed/i_tour_load.ddls.asddls b/tasks_gen/train/G1111/seed/i_tour_load.ddls.asddls new file mode 100644 index 0000000..f2b7fb7 --- /dev/null +++ b/tasks_gen/train/G1111/seed/i_tour_load.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tour load' +define view entity {{P}}I_TOUR_LOAD + as select from {{p}}tour as tour + inner join {{p}}parcel as parcel on parcel.tour_id = tour.tour_id +{ + key tour.tour_id as tour_id, + tour.driver as driver, + tour.depot as depot, + parcel.dest_zone as dest_zone, + count( * ) as parcel_count, + sum( parcel.weight ) as total_weight +} +group by + tour.tour_id, + tour.driver, + tour.depot, + parcel.dest_zone diff --git a/tasks_gen/train/G1111/seed/parcel.tabl.asabap b/tasks_gen/train/G1111/seed/parcel.tabl.asabap new file mode 100644 index 0000000..531d3bf --- /dev/null +++ b/tasks_gen/train/G1111/seed/parcel.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Parcel of a tour' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}parcel { + key client : abap.clnt not null; + key tour_id : abap.char(10) not null; + key parcel_no : abap.int4 not null; + dest_zone : abap.char(4); + weight : abap.dec(8,3); +} diff --git a/tasks_gen/train/G1111/seed/tour.tabl.asabap b/tasks_gen/train/G1111/seed/tour.tabl.asabap new file mode 100644 index 0000000..27a6cf8 --- /dev/null +++ b/tasks_gen/train/G1111/seed/tour.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Tour header' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}tour { + key client : abap.clnt not null; + key tour_id : abap.char(10) not null; + driver : abap.char(10); + depot : abap.char(4); + tour_date : abap.dats; +} diff --git a/tasks_gen/train/G1111/spec.md b/tasks_gen/train/G1111/spec.md new file mode 100644 index 0000000..7b76ce8 --- /dev/null +++ b/tasks_gen/train/G1111/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +The CDS view entity {{P}}I_TOUR_LOAD in package $TMP shows the load of the +delivery tours of a courier depot. Users report that the view gives wrong +numbers. Fix the view. The name of the view stays the same. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}TOUR (tour header), package $TMP. Key: TOUR_ID (CHAR 10). + Fields: DRIVER (CHAR 10), DEPOT (CHAR 4), TOUR_DATE (DATS). +- Table {{P}}PARCEL (parcel of a tour), package $TMP. Key: TOUR_ID (CHAR 10), + PARCEL_NO (INT4). Fields: DEST_ZONE (CHAR 4, destination zone of the + parcel), WEIGHT (DEC 8,3). +- The CDS view entity {{P}}I_TOUR_LOAD reads the two tables. +- The view is active, but the result is wrong. + +# 4. Contract +- Fix the CDS view entity {{P}}I_TOUR_LOAD in package $TMP. The object name + stays the same. +- Elements, with these names: TOUR_ID (key), DRIVER, DEPOT, PARCEL_COUNT, + TOTAL_WEIGHT. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each tour that has at least one parcel. +2. PARCEL_COUNT is the number of parcels of the tour. +3. TOTAL_WEIGHT is the sum of the weights of all parcels of the tour, with + 3 decimals. +4. The destination zone of a parcel must not change the result of the view. + A tour with parcels in more than one zone appears exactly once. +5. All other behavior of the view stays the same. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. The view must be active and must not have a + syntax error. +- Out of scope: do not change the tables {{P}}TOUR and {{P}}PARCEL. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1111/task.json b/tasks_gen/train/G1111/task.json new file mode 100644 index 0000000..36203af --- /dev/null +++ b/tasks_gen/train/G1111/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1111", + "category": "I", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}TOUR", + "file": "seed/tour.tabl.asabap", + "description": "Tour header" + }, + { + "type": "TABL", + "name": "{{P}}PARCEL", + "file": "seed/parcel.tabl.asabap", + "description": "Parcel of a tour" + }, + { + "type": "DDLS", + "name": "{{P}}I_TOUR_LOAD", + "file": "seed/i_tour_load.ddls.asddls", + "description": "Tour load (faulty: aggregated at zone level)" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_TOUR_LOAD", + "fields": [ + "TOUR_ID", + "DRIVER", + "DEPOT", + "PARCEL_COUNT", + "TOTAL_WEIGHT" + ] + } + ], + "out_of_scope": [ + "{{P}}TOUR", + "{{P}}PARCEL" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_TOUR_LOAD", + "file": "reference/i_tour_load.ddls.asddls", + "description": "Fixed tour load" + }, + { + "type": "CLAS", + "name": "{{P}}T31_TEST", + "file": "reference/t31_test.clas.abap", + "description": "T31 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1112/faulty/m0_i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/faulty/m0_i_blood_stock.ddls.asddls new file mode 100644 index 0000000..442da9d --- /dev/null +++ b/tasks_gen/train/G1112/faulty/m0_i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id <> donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + sum( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml > 0 +group by site.camp_id, site.city +having count( * ) >= 3 diff --git a/tasks_gen/train/G1112/faulty/m1_i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/faulty/m1_i_blood_stock.ddls.asddls new file mode 100644 index 0000000..cb4d0fd --- /dev/null +++ b/tasks_gen/train/G1112/faulty/m1_i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id = donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + max( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml > 0 +group by site.camp_id, site.city +having count( * ) >= 3 diff --git a/tasks_gen/train/G1112/faulty/m2_i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/faulty/m2_i_blood_stock.ddls.asddls new file mode 100644 index 0000000..892db88 --- /dev/null +++ b/tasks_gen/train/G1112/faulty/m2_i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id = donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + sum( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml <= 0 +group by site.camp_id, site.city +having count( * ) >= 3 diff --git a/tasks_gen/train/G1112/faulty/m3_i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/faulty/m3_i_blood_stock.ddls.asddls new file mode 100644 index 0000000..457776f --- /dev/null +++ b/tasks_gen/train/G1112/faulty/m3_i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id = donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + sum( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml > 0 +group by site.camp_id, site.city +having count( * ) < 3 diff --git a/tasks_gen/train/G1112/faulty/m4_i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/faulty/m4_i_blood_stock.ddls.asddls new file mode 100644 index 0000000..89b286c --- /dev/null +++ b/tasks_gen/train/G1112/faulty/m4_i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id = donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + sum( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml > 0 +group by site.camp_id, site.city +having count( * ) >= 4 diff --git a/tasks_gen/train/G1112/generation.json b/tasks_gen/train/G1112/generation.json new file mode 100644 index 0000000..557cda8 --- /dev/null +++ b/tasks_gen/train/G1112/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1112", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1112/hidden/blood_hidden.clas.abap b/tasks_gen/train/G1112/hidden/blood_hidden.clas.abap new file mode 100644 index 0000000..c0eeb8b --- /dev/null +++ b/tasks_gen/train/G1112/hidden/blood_hidden.clas.abap @@ -0,0 +1,95 @@ +CLASS {{p}}t_blood_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS counts_donations FOR TESTING. + METHODS sums_volume FOR TESTING. + METHODS camp_below_threshold FOR TESTING. + METHODS camp_without_donations FOR TESTING. + METHODS zero_volume_ignored FOR TESTING. + METHODS city_shown FOR TESTING. + METHODS harbour_count FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_blood_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BLOOD_STOCK' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_camp TYPE STANDARD TABLE OF {{p}}camp WITH EMPTY KEY. + DATA lt_donat TYPE STANDARD TABLE OF {{p}}donat WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_camp = VALUE #( ( camp_id = 'C1' city = 'NORTH' ) + ( camp_id = 'C2' city = 'SOUTH' ) + ( camp_id = 'C3' city = 'EAST' ) + ( camp_id = 'C4' city = 'WEST' ) + ( camp_id = 'C5' city = 'HARBOUR' ) ). + lt_donat = VALUE #( ( camp_id = 'C1' dona_no = 1 blood_group = 'A' vol_ml = 200 ) + ( camp_id = 'C1' dona_no = 2 blood_group = 'B' vol_ml = 250 ) + ( camp_id = 'C1' dona_no = 3 blood_group = 'O' vol_ml = 150 ) + ( camp_id = 'C2' dona_no = 1 blood_group = 'A' vol_ml = 300 ) + ( camp_id = 'C2' dona_no = 2 blood_group = 'A' vol_ml = 100 ) + ( camp_id = 'C3' dona_no = 1 blood_group = 'B' vol_ml = 0 ) + ( camp_id = 'C3' dona_no = 2 blood_group = 'B' vol_ml = 100 ) + ( camp_id = 'C3' dona_no = 3 blood_group = 'B' vol_ml = 100 ) + ( camp_id = 'C5' dona_no = 1 blood_group = 'O' vol_ml = 50 ) + ( camp_id = 'C5' dona_no = 2 blood_group = 'O' vol_ml = 50 ) + ( camp_id = 'C5' dona_no = 3 blood_group = 'O' vol_ml = 50 ) + ( camp_id = 'C5' dona_no = 4 blood_group = 'O' vol_ml = 50 ) + ( camp_id = 'C5' dona_no = 5 blood_group = 'O' vol_ml = 50 ) ). + go_env->insert_test_data( lt_camp ). + go_env->insert_test_data( lt_donat ). + ENDMETHOD. + + METHOD counts_donations. + SELECT SINGLE dona_count FROM {{p}}i_blood_stock WHERE camp_id = 'C1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = CONV decfloat34( lv_count ) ). + ENDMETHOD. + + METHOD sums_volume. + SELECT SINGLE total_volume FROM {{p}}i_blood_stock WHERE camp_id = 'C1' INTO @DATA(lv_vol). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 600 ) act = CONV decfloat34( lv_vol ) ). + ENDMETHOD. + + METHOD camp_below_threshold. + SELECT COUNT(*) FROM {{p}}i_blood_stock WHERE camp_id = 'C2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD camp_without_donations. + SELECT COUNT(*) FROM {{p}}i_blood_stock WHERE camp_id = 'C4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD zero_volume_ignored. + SELECT COUNT(*) FROM {{p}}i_blood_stock WHERE camp_id = 'C3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD city_shown. + SELECT SINGLE city FROM {{p}}i_blood_stock WHERE camp_id = 'C5' INTO @DATA(lv_city). + cl_abap_unit_assert=>assert_equals( exp = 'HARBOUR' act = lv_city ). + ENDMETHOD. + + METHOD harbour_count. + SELECT SINGLE dona_count, total_volume FROM {{p}}i_blood_stock WHERE camp_id = 'C5' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = CONV decfloat34( ls_row-dona_count ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 250 ) act = CONV decfloat34( ls_row-total_volume ) ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_blood_stock INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1112/mutation.json b/tasks_gen/train/G1112/mutation.json new file mode 100644 index 0000000..051dbf6 --- /dev/null +++ b/tasks_gen/train/G1112/mutation.json @@ -0,0 +1,72 @@ +{ + "task": "G1112", + "mutants": [ + { + "object": "{{P}}I_BLOOD_STOCK", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "CAMP_BELOW_THRESHOLD", + "CAMP_WITHOUT_DONATIONS", + "COUNTS_DONATIONS", + "HARBOUR_COUNT", + "ROW_COUNT", + "SUMS_VOLUME", + "ZERO_VOLUME_IGNORED" + ] + }, + { + "object": "{{P}}I_BLOOD_STOCK", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "HARBOUR_COUNT", + "SUMS_VOLUME" + ] + }, + { + "object": "{{P}}I_BLOOD_STOCK", + "mutant": "line 12: > -> <= (rel)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "CITY_SHOWN", + "COUNTS_DONATIONS", + "HARBOUR_COUNT", + "ROW_COUNT", + "SUMS_VOLUME" + ] + }, + { + "object": "{{P}}I_BLOOD_STOCK", + "mutant": "line 14: >= -> < (rel)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "CAMP_BELOW_THRESHOLD", + "CITY_SHOWN", + "COUNTS_DONATIONS", + "HARBOUR_COUNT", + "SUMS_VOLUME", + "ZERO_VOLUME_IGNORED" + ] + }, + { + "object": "{{P}}I_BLOOD_STOCK", + "mutant": "line 14: 3 -> 4 (const)", + "status": "killed", + "hidden": "5/8", + "failed_tests": [ + "COUNTS_DONATIONS", + "ROW_COUNT", + "SUMS_VOLUME" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1112/reference/blood_test.clas.abap b/tasks_gen/train/G1112/reference/blood_test.clas.abap new file mode 100644 index 0000000..0d7a206 --- /dev/null +++ b/tasks_gen/train/G1112/reference/blood_test.clas.abap @@ -0,0 +1,40 @@ +CLASS {{p}}t_blood_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS one_camp_total FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_blood_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BLOOD_STOCK' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD one_camp_total. + DATA lt_camp TYPE STANDARD TABLE OF {{p}}camp WITH EMPTY KEY. + DATA lt_donat TYPE STANDARD TABLE OF {{p}}donat WITH EMPTY KEY. + lt_camp = VALUE #( ( camp_id = 'A' city = 'RIVER' ) ). + lt_donat = VALUE #( ( camp_id = 'A' dona_no = 1 blood_group = 'A' vol_ml = 100 ) + ( camp_id = 'A' dona_no = 2 blood_group = 'A' vol_ml = 0 ) + ( camp_id = 'A' dona_no = 3 blood_group = 'A' vol_ml = 200 ) + ( camp_id = 'A' dona_no = 4 blood_group = 'A' vol_ml = 300 ) ). + go_env->insert_test_data( lt_camp ). + go_env->insert_test_data( lt_donat ). + SELECT SINGLE dona_count, total_volume FROM {{p}}i_blood_stock WHERE camp_id = 'A' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = CONV decfloat34( ls_row-dona_count ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 600 ) act = CONV decfloat34( ls_row-total_volume ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1112/reference/i_blood_stock.ddls.asddls b/tasks_gen/train/G1112/reference/i_blood_stock.ddls.asddls new file mode 100644 index 0000000..e507b97 --- /dev/null +++ b/tasks_gen/train/G1112/reference/i_blood_stock.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Blood donation totals' +define view entity {{P}}I_BLOOD_STOCK + as select from {{p}}donat as donation + inner join {{p}}camp as site on site.camp_id = donation.camp_id +{ + key site.camp_id as camp_id, + site.city as city, + count( * ) as dona_count, + sum( cast( donation.vol_ml as abap.dec( 15, 0 ) ) ) as total_volume +} +where donation.vol_ml > 0 +group by site.camp_id, site.city +having count( * ) >= 3 diff --git a/tasks_gen/train/G1112/seed/camp.tabl.asabap b/tasks_gen/train/G1112/seed/camp.tabl.asabap new file mode 100644 index 0000000..a8eeb5f --- /dev/null +++ b/tasks_gen/train/G1112/seed/camp.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Blood donation camp' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}camp { + key client : abap.clnt not null; + key camp_id : abap.char(10) not null; + city : abap.char(20); +} diff --git a/tasks_gen/train/G1112/seed/donat.tabl.asabap b/tasks_gen/train/G1112/seed/donat.tabl.asabap new file mode 100644 index 0000000..0e881a6 --- /dev/null +++ b/tasks_gen/train/G1112/seed/donat.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Blood donation' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}donat { + key client : abap.clnt not null; + key camp_id : abap.char(10) not null; + key dona_no : abap.int4 not null; + blood_group : abap.char(3); + vol_ml : abap.int4; +} diff --git a/tasks_gen/train/G1112/spec.md b/tasks_gen/train/G1112/spec.md new file mode 100644 index 0000000..537176c --- /dev/null +++ b/tasks_gen/train/G1112/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +Show the blood that each donation camp collected. A report and an OData +service will read the totals, so the calculation must be in the database, in a +CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}CAMP (donation camp), package $TMP. Key: CAMP_ID (CHAR 10). + Fields: CITY (CHAR 20). +- Table {{P}}DONAT (blood donation), package $TMP. Key: CAMP_ID (CHAR 10), + DONA_NO (INT4). Fields: BLOOD_GROUP (CHAR 3), VOL_ML (INT4, volume in + millilitres). + +# 4. Contract +- Create the CDS view entity {{P}}I_BLOOD_STOCK in package $TMP. +- Elements, with these names: CAMP_ID (key), CITY, DONA_COUNT, TOTAL_VOLUME. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. A donation is counted when its volume is greater than zero. A donation with + volume zero is not counted. +2. Show one row for each camp that has at least three counted donations. Do not + show a camp with fewer than three counted donations. +3. Do not show a camp that has no donation at all. +4. DONA_COUNT is the number of counted donations of the camp. +5. TOTAL_VOLUME is the sum of the volumes of the counted donations of the camp, + in millilitres. +6. CITY is the city of the camp. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}CAMP and {{P}}DONAT. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1112/task.json b/tasks_gen/train/G1112/task.json new file mode 100644 index 0000000..ce0ae91 --- /dev/null +++ b/tasks_gen/train/G1112/task.json @@ -0,0 +1,65 @@ +{ + "id": "G1112", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}CAMP", + "file": "seed/camp.tabl.asabap", + "description": "Blood donation camp" + }, + { + "type": "TABL", + "name": "{{P}}DONAT", + "file": "seed/donat.tabl.asabap", + "description": "Blood donation" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_BLOOD_STOCK", + "fields": [ + "CAMP_ID", + "CITY", + "DONA_COUNT", + "TOTAL_VOLUME" + ] + } + ], + "out_of_scope": [ + "{{P}}CAMP", + "{{P}}DONAT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_BLOOD_HIDDEN", + "file": "hidden/blood_hidden.clas.abap", + "description": "Hidden tests for the blood donation totals" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_BLOOD_STOCK", + "file": "reference/i_blood_stock.ddls.asddls", + "description": "Blood donation totals" + }, + { + "type": "CLAS", + "name": "{{P}}T_BLOOD_TEST", + "file": "reference/blood_test.clas.abap", + "description": "Own tests for the view" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1113/generation.json b/tasks_gen/train/G1113/generation.json new file mode 100644 index 0000000..db2a532 --- /dev/null +++ b/tasks_gen/train/G1113/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1113", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 49.4, + "null": 0 + }, + { + "stage": "validate", + "oracle": 98.5, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1113/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1113/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..c92b092 --- /dev/null +++ b/tasks_gen/train/G1113/hidden/t14_hidden.clas.abap @@ -0,0 +1,248 @@ +CLASS {{P}}T14_HIDDEN DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS line_for + IMPORTING iv_donor TYPE string + iv_group TYPE string + iv_units TYPE i + RETURNING VALUE(rv_line) TYPE string. + + METHODS sum_for + IMPORTING iv_donations TYPE string + iv_start TYPE string OPTIONAL + EXPORTING ev_totals TYPE string + ev_count TYPE i + ev_subrc TYPE sysubrc. + + METHODS sums_per_group FOR TESTING. + METHODS counts_donors FOR TESTING. + METHODS sorted_result FOR TESTING. + METHODS upper_case_group FOR TESTING. + METHODS replaces_content FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS invalid_units_raises FOR TESTING. + METHODS invalid_keeps_totals FOR TESTING. + METHODS donor_two_groups FOR TESTING. +ENDCLASS. + + +CLASS {{P}}T14_HIDDEN IMPLEMENTATION. + + METHOD line_for. + rv_line = iv_donor && ';' && iv_group && ';' && |{ iv_units }|. + ENDMETHOD. + + METHOD sum_for. + DATA lv_totals TYPE string. + lv_totals = iv_start. + + CALL FUNCTION '{{P}}DON_SUM' + EXPORTING + iv_donations = iv_donations + IMPORTING + ev_donor_count = ev_count + CHANGING + cv_totals = lv_totals + EXCEPTIONS + invalid_units = 1 + OTHERS = 2. + + ev_subrc = sy-subrc. + ev_totals = lv_totals. + ENDMETHOD. + + METHOD sums_per_group. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 3 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'O-' iv_units = 1 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 5 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'O-' iv_units = 1 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + + METHOD counts_donors. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D3' iv_group = 'B' iv_units = 1 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D3' iv_group = 'B' iv_units = 1 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + + METHOD sorted_result. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 1 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 1 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + + METHOD upper_case_group. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'a+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 3 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 5 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + + METHOD replaces_content. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ). + + sum_for( EXPORTING iv_donations = lv_input + iv_start = 'OLD TOTALS' + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + + METHOD empty_input. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + sum_for( EXPORTING iv_donations = '' + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = '' act = lv_actual ). + ENDMETHOD. + + METHOD invalid_units_raises. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 0 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD invalid_keeps_totals. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 0 ). + + sum_for( EXPORTING iv_donations = lv_input + iv_start = 'KEEP' + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 'KEEP' act = lv_actual ). + ENDMETHOD. + + METHOD donor_two_groups. + DATA lv_actual TYPE string. + DATA lv_count TYPE i. + DATA lv_subrc TYPE sysubrc. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 3 ). + + sum_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_actual + ev_count = lv_count + ev_subrc = lv_subrc ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 3 ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_actual ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1113/reference/don_sum.func.abap b/tasks_gen/train/G1113/reference/don_sum.func.abap new file mode 100644 index 0000000..9c19dd9 --- /dev/null +++ b/tasks_gen/train/G1113/reference/don_sum.func.abap @@ -0,0 +1,70 @@ +FUNCTION {{P}}DON_SUM + IMPORTING + iv_donations TYPE string + EXPORTING + ev_donor_count TYPE i + CHANGING + cv_totals TYPE string + EXCEPTIONS + invalid_units. + + TYPES: + BEGIN OF ty_total, + donor_id TYPE string, + group_code TYPE string, + total_units TYPE i, + END OF ty_total. + + DATA lt_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + DATA lt_totals TYPE SORTED TABLE OF ty_total + WITH UNIQUE KEY donor_id group_code. + DATA lv_line TYPE string. + DATA lv_donor TYPE string. + DATA lv_group TYPE string. + DATA lv_units TYPE string. + DATA lv_units_int TYPE i. + DATA lv_result TYPE string. + DATA lv_prev TYPE string. + + IF iv_donations IS NOT INITIAL. + SPLIT iv_donations AT cl_abap_char_utilities=>newline INTO TABLE lt_lines. + ENDIF. + + LOOP AT lt_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_donor lv_group lv_units. + + lv_units_int = lv_units. + IF lv_units_int < 1. + RAISE invalid_units. + ENDIF. + + lv_group = to_upper( lv_group ). + + READ TABLE lt_totals ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY donor_id = lv_donor group_code = lv_group. + IF sy-subrc <> 0. + INSERT VALUE #( donor_id = lv_donor + group_code = lv_group + total_units = 0 ) + INTO TABLE lt_totals ASSIGNING . + ENDIF. + -total_units = -total_units + lv_units_int. + ENDLOOP. + + LOOP AT lt_totals INTO DATA(ls_total). + IF ls_total-donor_id <> lv_prev. + ev_donor_count = ev_donor_count + 1. + lv_prev = ls_total-donor_id. + ENDIF. + + DATA(lv_out) = |{ ls_total-donor_id };{ ls_total-group_code };{ ls_total-total_units }|. + IF lv_result IS INITIAL. + lv_result = lv_out. + ELSE. + lv_result = lv_result && cl_abap_char_utilities=>newline && lv_out. + ENDIF. + ENDLOOP. + + cv_totals = lv_result. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1113/reference/t14_test.clas.abap b/tasks_gen/train/G1113/reference/t14_test.clas.abap new file mode 100644 index 0000000..63357a1 --- /dev/null +++ b/tasks_gen/train/G1113/reference/t14_test.clas.abap @@ -0,0 +1,120 @@ +CLASS {{P}}T14_TEST DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS line_for + IMPORTING iv_donor TYPE string + iv_group TYPE string + iv_units TYPE i + RETURNING VALUE(rv_line) TYPE string. + + METHODS totals_for + IMPORTING iv_donations TYPE string + EXPORTING ev_totals TYPE string + ev_count TYPE i. + + METHODS groups_donations FOR TESTING. + METHODS counts_donors FOR TESTING. + METHODS rejects_zero_units FOR TESTING. + METHODS empty_input FOR TESTING. +ENDCLASS. + + +CLASS {{P}}T14_TEST IMPLEMENTATION. + + METHOD line_for. + rv_line = iv_donor && ';' && iv_group && ';' && |{ iv_units }|. + ENDMETHOD. + + METHOD totals_for. + DATA lv_totals TYPE string. + + CALL FUNCTION '{{P}}DON_SUM' + EXPORTING + iv_donations = iv_donations + IMPORTING + ev_donor_count = ev_count + CHANGING + cv_totals = lv_totals + EXCEPTIONS + invalid_units = 1 + OTHERS = 2. + + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ev_totals = lv_totals. + ENDMETHOD. + + METHOD groups_donations. + DATA lv_totals TYPE string. + DATA lv_count TYPE i. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 2 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'a+' iv_units = 3 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'O-' iv_units = 1 ). + + totals_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_totals + ev_count = lv_count ). + + DATA(lv_expected) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 5 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'O-' iv_units = 1 ). + + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_totals ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD counts_donors. + DATA lv_totals TYPE string. + DATA lv_count TYPE i. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D1' iv_group = 'O-' iv_units = 1 ) + && cl_abap_char_utilities=>newline + && line_for( iv_donor = 'D2' iv_group = 'A+' iv_units = 1 ). + + totals_for( EXPORTING iv_donations = lv_input + IMPORTING ev_totals = lv_totals + ev_count = lv_count ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + cl_abap_unit_assert=>assert_equals( + exp = 3 + act = strlen( lv_totals ) - strlen( replace( val = lv_totals sub = cl_abap_char_utilities=>newline with = '' occ = 0 ) ) + 1 ). + ENDMETHOD. + + METHOD rejects_zero_units. + DATA lv_totals TYPE string. + DATA lv_count TYPE i. + + DATA(lv_input) = line_for( iv_donor = 'D1' iv_group = 'A+' iv_units = 0 ). + + CALL FUNCTION '{{P}}DON_SUM' + EXPORTING + iv_donations = lv_input + IMPORTING + ev_donor_count = lv_count + CHANGING + cv_totals = lv_totals + EXCEPTIONS + invalid_units = 1 + OTHERS = 2. + + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. + + METHOD empty_input. + DATA lv_totals TYPE string. + DATA lv_count TYPE i. + + totals_for( EXPORTING iv_donations = '' + IMPORTING ev_totals = lv_totals + ev_count = lv_count ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ). + cl_abap_unit_assert=>assert_equals( exp = '' act = lv_totals ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1113/seed/don_sample.func.abap b/tasks_gen/train/G1113/seed/don_sample.func.abap new file mode 100644 index 0000000..ead5215 --- /dev/null +++ b/tasks_gen/train/G1113/seed/don_sample.func.abap @@ -0,0 +1,18 @@ +FUNCTION {{P}}DON_SAMPLE + IMPORTING + iv_donations TYPE string + EXPORTING + ev_line_count TYPE i + ev_first_line TYPE string. + + DATA lt_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + IF iv_donations IS NOT INITIAL. + SPLIT iv_donations AT cl_abap_char_utilities=>newline INTO TABLE lt_lines. + ENDIF. + + ev_line_count = lines( lt_lines ). + + READ TABLE lt_lines INTO ev_first_line INDEX 1. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1113/spec.md b/tasks_gen/train/G1113/spec.md new file mode 100644 index 0000000..ab660b7 --- /dev/null +++ b/tasks_gen/train/G1113/spec.md @@ -0,0 +1,58 @@ +# 1. Goal +A blood donation centre summarises the donations of one collection day. The +centre calls one function module for each collection day. The function module +groups the donations by donor and blood group and gives the total units of each +group. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_DON exists in package $TMP. +- The collection team records the donations of one day on a handheld device. + The device sends the donations of the day as one text. +- Each donation is one line of the text. A line has three fields that are + separated by a semicolon (;): + - field 1: the donor id, without blanks + - field 2: the blood group code, without blanks + - field 3: the units, a decimal number without leading zeros +- The lines of the text are separated by the newline character + (cl_abap_char_utilities=>newline). The text does not end with a newline. +- Function module {{P}}DON_SAMPLE exists in the group. It shows how the text of + one day is split into lines. + +# 4. Contract +- Create the function module {{P}}DON_SUM in function group {{P}}FG_DON. +- Use this interface: + - IMPORTING iv_donations TYPE string + - EXPORTING ev_donor_count TYPE i + - CHANGING cv_totals TYPE string + - EXCEPTIONS invalid_units + +# 5. Business rules +1. A donation is not valid if its units are less than 1. If the text contains a + donation that is not valid, raise the exception INVALID_UNITS. In this case, + do not change cv_totals. +2. Group the donations by donor id and blood group code. The total units of a + group is the sum of the units of all donations of the group. +3. Compare blood group codes without regard to case. Write the blood group code + in the result in upper case. +4. cv_totals has exactly one line for each group. Replace the content of + cv_totals. Do not append to the old content. +5. A line of cv_totals has the same layout as a line of the input text: donor + id, semicolon, blood group code, semicolon, total units. The total units have + no leading zeros. The lines are sorted by donor id in ascending order and + then by blood group code in ascending order. The lines are separated by the + newline character. The text does not end with a newline. +6. ev_donor_count is the number of different donor id values in the text. +7. If the text is empty, cv_totals is empty and ev_donor_count is 0. + +# 6. Constraints +- Release target: 8.16. +- Coding standard: Clean ABAP. Methods have less than 40 statements. +- Out of scope: do not change {{P}}DON_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1113/task.json b/tasks_gen/train/G1113/task.json new file mode 100644 index 0000000..ff7b0b1 --- /dev/null +++ b/tasks_gen/train/G1113/task.json @@ -0,0 +1,77 @@ +{ + "id": "G1113", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_DON", + "description": "Function group for blood donation summaries" + }, + { + "type": "FUNC", + "name": "{{P}}DON_SAMPLE", + "functionGroup": "{{P}}FG_DON", + "file": "seed/don_sample.func.abap", + "description": "Sample module that splits a donation text into lines" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}DON_SUM", + "functionGroup": "{{P}}FG_DON", + "params": [ + { + "name": "iv_donations", + "type": "string" + }, + { + "name": "ev_donor_count", + "type": "i" + }, + { + "name": "cv_totals", + "type": "string" + } + ] + } + ], + "out_of_scope": [ + "{{P}}DON_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}DON_SUM", + "functionGroup": "{{P}}FG_DON", + "file": "reference/don_sum.func.abap", + "description": "Donation summary module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length", + "no_nested_loops" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1114/faulty/m0_bakery_bill_old.prog.abap b/tasks_gen/train/G1114/faulty/m0_bakery_bill_old.prog.abap new file mode 100644 index 0000000..cfebd8f --- /dev/null +++ b/tasks_gen/train/G1114/faulty/m0_bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active <> 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + AND deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls <= 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + + ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + + ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/faulty/m1_bakery_bill_old.prog.abap b/tasks_gen/train/G1114/faulty/m1_bakery_bill_old.prog.abap new file mode 100644 index 0000000..d35a258 --- /dev/null +++ b/tasks_gen/train/G1114/faulty/m1_bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active = 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + OR deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls <= 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + + ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + + ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/faulty/m2_bakery_bill_old.prog.abap b/tasks_gen/train/G1114/faulty/m2_bakery_bill_old.prog.abap new file mode 100644 index 0000000..080be88 --- /dev/null +++ b/tasks_gen/train/G1114/faulty/m2_bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active = 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + AND deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls > 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + + ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + + ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/faulty/m3_bakery_bill_old.prog.abap b/tasks_gen/train/G1114/faulty/m3_bakery_bill_old.prog.abap new file mode 100644 index 0000000..1dc2f74 --- /dev/null +++ b/tasks_gen/train/G1114/faulty/m3_bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active = 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + AND deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls <= 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + - ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + + ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/faulty/m4_bakery_bill_old.prog.abap b/tasks_gen/train/G1114/faulty/m4_bakery_bill_old.prog.abap new file mode 100644 index 0000000..7e7e280 --- /dev/null +++ b/tasks_gen/train/G1114/faulty/m4_bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active = 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + AND deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls <= 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + + ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + - ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/generation.json b/tasks_gen/train/G1114/generation.json new file mode 100644 index 0000000..ae5bdd0 --- /dev/null +++ b/tasks_gen/train/G1114/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1114", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0190 (similarity spec 0.53, rules 0.61, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1114/hidden/bakery_hidden.clas.abap b/tasks_gen/train/G1114/hidden/bakery_hidden.clas.abap new file mode 100644 index 0000000..82798bb --- /dev/null +++ b/tasks_gen/train/G1114/hidden/bakery_hidden.clas.abap @@ -0,0 +1,117 @@ +CLASS {{p}}bakery_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_shop TYPE RANGE OF {{p}}shop-shop_id. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_shop TYPE tt_shop OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS all_shops_jan FOR TESTING. + METHODS sorted_by_total FOR TESTING. + METHODS fee_and_counts FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS shop_filter FOR TESTING. + METHODS inactive_shop FOR TESTING. + METHODS empty_delivery FOR TESTING. +ENDCLASS. + + +CLASS {{p}}bakery_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}bakery_bill_old + WITH p_from = iv_from + WITH p_to = iv_to + WITH s_shop IN it_shop + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } is missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_shops_jan. + cl_abap_unit_assert=>assert_equals( exp = 2 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ). + ENDMETHOD. + + METHOD sorted_by_total. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = `S2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SHOP_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `96.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + cl_abap_unit_assert=>assert_equals( exp = `S1` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `SHOP_ID` ) ). + ENDMETHOD. + + METHOD fee_and_counts. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `S1` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SHOP_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `North Shop` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SHOP_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `15` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LOAVES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `20` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ROLLS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `43.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260201' iv_to = '20260228' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `S2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SHOP_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `62.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + ENDMETHOD. + + METHOD shop_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_shop = VALUE #( ( sign = 'I' option = 'EQ' low = 'S2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `24.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + ENDMETHOD. + + METHOD inactive_shop. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_shop = VALUE #( ( sign = 'I' option = 'EQ' low = 'S3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( lr_data ) ). + ENDMETHOD. + + METHOD empty_delivery. + DATA(lr_data) = run( iv_from = '20260120' iv_to = '20260120' + it_shop = VALUE #( ( sign = 'I' option = 'EQ' low = 'S1' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( lr_data ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/mutation.json b/tasks_gen/train/G1114/mutation.json new file mode 100644 index 0000000..4267de9 --- /dev/null +++ b/tasks_gen/train/G1114/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1114", + "mutants": [ + { + "object": "{{P}}BAKERY_BILL_OLD", + "mutant": "line 59: = -> <> (eq)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "ALL_SHOPS_JAN", + "DATE_FILTER", + "FEE_AND_COUNTS", + "INACTIVE_SHOP", + "SHOP_FILTER", + "SORTED_BY_TOTAL" + ] + }, + { + "object": "{{P}}BAKERY_BILL_OLD", + "mutant": "line 67: AND -> OR (logic)", + "status": "killed", + "hidden": "3/7", + "failed_tests": [ + "DATE_FILTER", + "EMPTY_DELIVERY", + "FEE_AND_COUNTS", + "SHOP_FILTER" + ] + }, + { + "object": "{{P}}BAKERY_BILL_OLD", + "mutant": "line 74: <= -> > (rel)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "ALL_SHOPS_JAN", + "DATE_FILTER", + "EMPTY_DELIVERY", + "FEE_AND_COUNTS", + "SHOP_FILTER", + "SORTED_BY_TOTAL" + ] + }, + { + "object": "{{P}}BAKERY_BILL_OLD", + "mutant": "line 90: + -> - (arith)", + "status": "killed", + "hidden": "3/7", + "failed_tests": [ + "DATE_FILTER", + "FEE_AND_COUNTS", + "SHOP_FILTER", + "SORTED_BY_TOTAL" + ] + }, + { + "object": "{{P}}BAKERY_BILL_OLD", + "mutant": "line 92: + -> - (arith)", + "status": "killed", + "hidden": "3/7", + "failed_tests": [ + "DATE_FILTER", + "FEE_AND_COUNTS", + "SHOP_FILTER", + "SORTED_BY_TOTAL" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1114/reference/bakery_bill_old.prog.abap b/tasks_gen/train/G1114/reference/bakery_bill_old.prog.abap new file mode 100644 index 0000000..d3b927d --- /dev/null +++ b/tasks_gen/train/G1114/reference/bakery_bill_old.prog.abap @@ -0,0 +1,181 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_shop, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaf_price TYPE {{p}}shop-loaf_price, + roll_price TYPE {{p}}shop-roll_price, + deliv_fee TYPE {{p}}shop-deliv_fee, + END OF ty_shop, + tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, + BEGIN OF ty_deliv, + shop_id TYPE {{p}}deliv-shop_id, + loaves TYPE {{p}}deliv-loaves, + rolls TYPE {{p}}deliv-rolls, + END OF ty_deliv, + tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, + tt_range TYPE RANGE OF {{p}}shop-shop_id. + + METHODS read_shops + IMPORTING it_range TYPE tt_range + RETURNING VALUE(rt_shop) TYPE tt_shop. + + METHODS read_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_deliv) TYPE tt_deliv. + + METHODS calculate + IMPORTING it_shop TYPE tt_shop + it_deliv TYPE tt_deliv + RETURNING VALUE(rt_line) TYPE tt_line. + + METHODS display + CHANGING ct_line TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD read_shops. + SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee + FROM {{p}}shop + WHERE shop_id IN @it_range + AND is_active = 'X' + INTO TABLE @rt_shop. + ENDMETHOD. + + METHOD read_deliveries. + SELECT shop_id, loaves, rolls + FROM {{p}}deliv + WHERE deliv_date >= @iv_from + AND deliv_date <= @iv_to + INTO TABLE @rt_deliv. + ENDMETHOD. + + METHOD calculate. + DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. + LOOP AT it_deliv INTO DATA(ls_deliv). + IF ls_deliv-loaves + ls_deliv-rolls <= 0. + CONTINUE. + ENDIF. + READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. + IF sy-subrc <> 0. + INSERT VALUE #( shop_id = ls_shop-shop_id + shop_name = ls_shop-shop_name ) + INTO TABLE lt_sum ASSIGNING . + ENDIF. + -loaves = -loaves + ls_deliv-loaves. + -rolls = -rolls + ls_deliv-rolls. + -total = -total + + ls_deliv-loaves * ls_shop-loaf_price + + ls_deliv-rolls * ls_shop-roll_price + + ls_shop-deliv_fee. + ENDLOOP. + rt_line = CORRESPONDING #( lt_sum ). + SORT rt_line BY total DESCENDING shop_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_line ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + lo_billing->display( CHANGING ct_line = lt_line ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_shop FOR TESTING. + METHODS skips_empty_delivery FOR TESTING. + METHODS skips_inactive_shop FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). + + DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. + lt_shop = VALUE #( + ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) + ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) + ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). + go_env->insert_test_data( lt_shop ). + + DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. + lt_deliv = VALUE #( + ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) + ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) + ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) + ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). + go_env->insert_test_data( lt_deliv ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). + ENDMETHOD. + + METHOD skips_empty_delivery. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. + + METHOD skips_inactive_shop. + DATA(lo_billing) = NEW lcl_billing( ). + DATA(lt_shop) = lo_billing->read_shops( + VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). + DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). + DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/seed/bakery_bill_old.prog.abap b/tasks_gen/train/G1114/seed/bakery_bill_old.prog.abap new file mode 100644 index 0000000..5445e6e --- /dev/null +++ b/tasks_gen/train/G1114/seed/bakery_bill_old.prog.abap @@ -0,0 +1,74 @@ +REPORT {{p}}bakery_bill_old. + +DATA: gv_shop_id TYPE {{p}}shop-shop_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_shop FOR gv_shop_id. + +TYPES: BEGIN OF ty_line, + shop_id TYPE {{p}}shop-shop_id, + shop_name TYPE {{p}}shop-shop_name, + loaves TYPE i, + rolls TYPE i, + total TYPE {{p}}shop-loaf_price, + END OF ty_line. + +DATA: gt_shop TYPE STANDARD TABLE OF {{p}}shop WITH HEADER LINE, + gt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH HEADER LINE, + gt_out TYPE STANDARD TABLE OF ty_line WITH HEADER LINE. + +START-OF-SELECTION. + PERFORM read_shops. + PERFORM read_deliveries. + PERFORM calc_totals. + PERFORM display. + +FORM read_shops. + SELECT * FROM {{p}}shop INTO TABLE gt_shop + WHERE shop_id IN s_shop + AND is_active = 'X'. + SORT gt_shop BY shop_id. +ENDFORM. + +FORM read_deliveries. + SELECT * FROM {{p}}deliv INTO TABLE gt_deliv + WHERE deliv_date >= p_from + AND deliv_date <= p_to. + SORT gt_deliv BY shop_id. +ENDFORM. + +FORM calc_totals. + DATA: ls_out TYPE ty_line, + lv_fee TYPE {{p}}shop-loaf_price. + CLEAR gt_out. + LOOP AT gt_shop. + CLEAR ls_out. + ls_out-shop_id = gt_shop-shop_id. + ls_out-shop_name = gt_shop-shop_name. + LOOP AT gt_deliv WHERE shop_id = gt_shop-shop_id. + IF gt_deliv-loaves + gt_deliv-rolls > 0. + ls_out-loaves = ls_out-loaves + gt_deliv-loaves. + ls_out-rolls = ls_out-rolls + gt_deliv-rolls. + lv_fee = gt_deliv-loaves * gt_shop-loaf_price + + gt_deliv-rolls * gt_shop-roll_price + + gt_shop-deliv_fee. + ls_out-total = ls_out-total + lv_fee. + ENDIF. + ENDLOOP. + IF ls_out-loaves + ls_out-rolls > 0. + APPEND ls_out TO gt_out. + ENDIF. + ENDLOOP. + SORT gt_out BY total DESCENDING shop_id ASCENDING. +ENDFORM. + +FORM display. + DATA lo_alv TYPE REF TO cl_salv_table. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv + CHANGING t_table = gt_out[] ). + lo_alv->display( ). + CATCH cx_salv_msg. + ENDTRY. +ENDFORM. diff --git a/tasks_gen/train/G1114/seed/bakery_seed.clas.abap b/tasks_gen/train/G1114/seed/bakery_seed.clas.abap new file mode 100644 index 0000000..c944da7 --- /dev/null +++ b/tasks_gen/train/G1114/seed/bakery_seed.clas.abap @@ -0,0 +1,27 @@ +CLASS {{p}}bakery_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}bakery_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}deliv. + DELETE FROM {{p}}shop. + + INSERT {{p}}shop FROM TABLE @( VALUE #( + ( shop_id = 'S1' shop_name = 'North Shop' loaf_price = '2.00' roll_price = '0.50' deliv_fee = '1.50' is_active = 'X' ) + ( shop_id = 'S2' shop_name = 'South Shop' loaf_price = '3.00' roll_price = '1.00' deliv_fee = '2.00' is_active = 'X' ) + ( shop_id = 'S3' shop_name = 'Closed Shop' loaf_price = '1.00' roll_price = '0.20' deliv_fee = '0.50' is_active = ' ' ) ) ). + + INSERT {{p}}deliv FROM TABLE @( VALUE #( + ( deliv_id = 1 shop_id = 'S1' deliv_date = '20260110' loaves = 10 rolls = 20 ) + ( deliv_id = 2 shop_id = 'S1' deliv_date = '20260112' loaves = 5 rolls = 0 ) + ( deliv_id = 3 shop_id = 'S2' deliv_date = '20260111' loaves = 4 rolls = 10 ) + ( deliv_id = 4 shop_id = 'S2' deliv_date = '20260215' loaves = 20 rolls = 0 ) + ( deliv_id = 5 shop_id = 'S3' deliv_date = '20260110' loaves = 6 rolls = 6 ) + ( deliv_id = 6 shop_id = 'S1' deliv_date = '20260120' loaves = 0 rolls = 0 ) + ( deliv_id = 7 shop_id = 'S2' deliv_date = '20251220' loaves = 2 rolls = 2 ) ) ). + + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1114/seed/deliv.tabl.asabap b/tasks_gen/train/G1114/seed/deliv.tabl.asabap new file mode 100644 index 0000000..0e86deb --- /dev/null +++ b/tasks_gen/train/G1114/seed/deliv.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Delivery' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}deliv { + key client : abap.clnt not null; + key deliv_id : abap.int4 not null; + shop_id : abap.char(4); + deliv_date : abap.dats; + loaves : abap.int4; + rolls : abap.int4; +} diff --git a/tasks_gen/train/G1114/seed/shop.tabl.asabap b/tasks_gen/train/G1114/seed/shop.tabl.asabap new file mode 100644 index 0000000..831eaec --- /dev/null +++ b/tasks_gen/train/G1114/seed/shop.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Shop' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}shop { + key client : abap.clnt not null; + key shop_id : abap.char(4) not null; + shop_name : abap.char(30); + loaf_price : abap.dec(15,2); + roll_price : abap.dec(15,2); + deliv_fee : abap.dec(15,2); + is_active : abap.char(1); +} diff --git a/tasks_gen/train/G1114/spec.md b/tasks_gen/train/G1114/spec.md new file mode 100644 index 0000000..da93b70 --- /dev/null +++ b/tasks_gen/train/G1114/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +Refactor the legacy report {{P}}BAKERY_BILL_OLD in package $TMP. A bakery bills +its shops for the bread and the rolls that it delivered in a period. The report +must keep its behavior and become maintainable. + +# 2. Open questions +None. + +# 3. Context +- The report {{P}}BAKERY_BILL_OLD is active in package $TMP. It is legacy code. +- The report reads the tables {{P}}SHOP and {{P}}DELIV. Both tables contain data. +- The report shows an ALV list. + +# 4. Contract +- Keep the report {{P}}BAKERY_BILL_OLD in package $TMP. Keep the name. +- Keep the selection screen: + - Parameter P_FROM, type DATS, mandatory: the first delivery date to include. + - Parameter P_TO, type DATS, mandatory: the last delivery date to include. + - Select-option S_SHOP for the shop. +- Keep the ALV output. The output table has these columns, with these names: + SHOP_ID, SHOP_NAME, LOAVES, ROLLS, TOTAL. + +# 5. Business rules +The behavior stays the same. The legacy code contains the rules. Read the +legacy code and keep every rule, every filter and every value. Do not change +the selection screen, the output columns or the result rows. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Quality goals: + - Read each of the two tables with one database access. + - Do not use a nested loop over the two tables. The runtime must grow with + the number of rows, not with the product of the rows. + - Do not use obsolete language elements. No FORM routines, no header-line + tables, no SELECT *. + - Put the logic in a local class. Keep every method below 40 statements. +- Out of scope: do not change the tables {{P}}SHOP and {{P}}DELIV. + +# 7. Acceptance +- The report is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1114/task.json b/tasks_gen/train/G1114/task.json new file mode 100644 index 0000000..1639135 --- /dev/null +++ b/tasks_gen/train/G1114/task.json @@ -0,0 +1,75 @@ +{ + "id": "G1114", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SHOP", + "file": "seed/shop.tabl.asabap", + "description": "Shops that receive the bakery deliveries" + }, + { + "type": "TABL", + "name": "{{P}}DELIV", + "file": "seed/deliv.tabl.asabap", + "description": "Bread and roll deliveries of the bakery" + }, + { + "type": "PROG", + "name": "{{P}}BAKERY_BILL_OLD", + "file": "seed/bakery_bill_old.prog.abap", + "description": "Legacy billing report of the bakery" + }, + { + "type": "CLAS", + "name": "{{P}}BAKERY_SEED", + "file": "seed/bakery_seed.clas.abap", + "description": "Seed data for the shops and the deliveries", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}BAKERY_BILL_OLD", + "parameters": [ + "p_from", + "p_to", + "s_shop" + ] + } + ], + "out_of_scope": [ + "{{P}}SHOP", + "{{P}}DELIV" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}BAKERY_HIDDEN", + "file": "hidden/bakery_hidden.clas.abap", + "description": "Hidden tests for the refactored bakery report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}BAKERY_BILL_OLD", + "file": "reference/bakery_bill_old.prog.abap", + "description": "Refactored bakery report with local test classes" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length", + "nested_loops" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1115/faulty/m0_trans_pricer.clas.abap b/tasks_gen/train/G1115/faulty/m0_trans_pricer.clas.abap new file mode 100644 index 0000000..89e3014 --- /dev/null +++ b/tasks_gen/train/G1115/faulty/m0_trans_pricer.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}trans_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. + METHODS calculate_fee + IMPORTING iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + + PRIVATE SECTION. + CONSTANTS c_rate_std TYPE decfloat34 VALUE '0.12'. + CONSTANTS c_rate_rare TYPE decfloat34 VALUE '0.19'. + CONSTANTS c_min_charge TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_certified_fee TYPE decfloat34 VALUE '22.00'. + CONSTANTS c_express_percent TYPE decfloat34 VALUE '150'. + CONSTANTS c_tier_one_words TYPE i VALUE 1000. + CONSTANTS c_tier_two_words TYPE i VALUE 5000. + CONSTANTS c_tier_one_percent TYPE decfloat34 VALUE '10'. + CONSTANTS c_tier_two_percent TYPE decfloat34 VALUE '15'. + + METHODS rate + IMPORTING iv_lang_pair TYPE string + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS translation_charge + IMPORTING iv_word_count TYPE i + iv_rate TYPE decfloat34 + RETURNING VALUE(rv_charge) TYPE decfloat34. + METHODS discount + IMPORTING iv_word_count TYPE i + iv_charge TYPE decfloat34 + RETURNING VALUE(rv_discount) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}trans_pricer IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_fee. + DATA(lv_amount) = translation_charge( + iv_word_count = iv_word_count + iv_rate = rate( iv_lang_pair ) ). + lv_amount = lv_amount - discount( iv_word_count = iv_word_count + iv_charge = lv_amount ). + IF iv_certified <> abap_true. + lv_amount = lv_amount + c_certified_fee. + ENDIF. + IF iv_express = abap_true. + lv_amount = round( val = lv_amount * c_express_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + rv_fee = lv_amount. + ENDMETHOD. + + METHOD rate. + CASE iv_lang_pair. + WHEN 'STD'. + rv_rate = c_rate_std. + WHEN 'RARE'. + rv_rate = c_rate_rare. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD translation_charge. + rv_charge = round( val = iv_word_count * iv_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD discount. + IF iv_word_count >= c_tier_two_words. + rv_discount = round( val = iv_charge * c_tier_two_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ELSEIF iv_word_count >= c_tier_one_words. + rv_discount = round( val = iv_charge * c_tier_one_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/faulty/m1_trans_fee.func.abap b/tasks_gen/train/G1115/faulty/m1_trans_fee.func.abap new file mode 100644 index 0000000..8b6a825 --- /dev/null +++ b/tasks_gen/train/G1115/faulty/m1_trans_fee.func.abap @@ -0,0 +1,28 @@ +FUNCTION {{p}}trans_fee + IMPORTING + iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_lang_pair + invalid_word_count. + + IF iv_lang_pair = 'STD' AND iv_lang_pair <> 'RARE'. + RAISE invalid_lang_pair. + ENDIF. + + IF iv_word_count < 1. + RAISE invalid_word_count. + ENDIF. + + DATA(lo_pricer) = NEW {{p}}trans_pricer( ). + ev_fee = lo_pricer->calculate_fee( + iv_lang_pair = iv_lang_pair + iv_word_count = iv_word_count + iv_express = iv_express + iv_certified = iv_certified ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1115/faulty/m2_trans_pricer.clas.abap b/tasks_gen/train/G1115/faulty/m2_trans_pricer.clas.abap new file mode 100644 index 0000000..6f24084 --- /dev/null +++ b/tasks_gen/train/G1115/faulty/m2_trans_pricer.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}trans_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. + METHODS calculate_fee + IMPORTING iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + + PRIVATE SECTION. + CONSTANTS c_rate_std TYPE decfloat34 VALUE '0.12'. + CONSTANTS c_rate_rare TYPE decfloat34 VALUE '0.19'. + CONSTANTS c_min_charge TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_certified_fee TYPE decfloat34 VALUE '22.00'. + CONSTANTS c_express_percent TYPE decfloat34 VALUE '150'. + CONSTANTS c_tier_one_words TYPE i VALUE 1000. + CONSTANTS c_tier_two_words TYPE i VALUE 5000. + CONSTANTS c_tier_one_percent TYPE decfloat34 VALUE '10'. + CONSTANTS c_tier_two_percent TYPE decfloat34 VALUE '15'. + + METHODS rate + IMPORTING iv_lang_pair TYPE string + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS translation_charge + IMPORTING iv_word_count TYPE i + iv_rate TYPE decfloat34 + RETURNING VALUE(rv_charge) TYPE decfloat34. + METHODS discount + IMPORTING iv_word_count TYPE i + iv_charge TYPE decfloat34 + RETURNING VALUE(rv_discount) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}trans_pricer IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_fee. + DATA(lv_amount) = translation_charge( + iv_word_count = iv_word_count + iv_rate = rate( iv_lang_pair ) ). + lv_amount = lv_amount - discount( iv_word_count = iv_word_count + iv_charge = lv_amount ). + IF iv_certified = abap_true. + lv_amount = lv_amount + c_certified_fee. + ENDIF. + IF iv_express = abap_false. + lv_amount = round( val = lv_amount * c_express_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + rv_fee = lv_amount. + ENDMETHOD. + + METHOD rate. + CASE iv_lang_pair. + WHEN 'STD'. + rv_rate = c_rate_std. + WHEN 'RARE'. + rv_rate = c_rate_rare. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD translation_charge. + rv_charge = round( val = iv_word_count * iv_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD discount. + IF iv_word_count >= c_tier_two_words. + rv_discount = round( val = iv_charge * c_tier_two_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ELSEIF iv_word_count >= c_tier_one_words. + rv_discount = round( val = iv_charge * c_tier_one_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/faulty/m3_trans_fee.func.abap b/tasks_gen/train/G1115/faulty/m3_trans_fee.func.abap new file mode 100644 index 0000000..64ea8b6 --- /dev/null +++ b/tasks_gen/train/G1115/faulty/m3_trans_fee.func.abap @@ -0,0 +1,28 @@ +FUNCTION {{p}}trans_fee + IMPORTING + iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_lang_pair + invalid_word_count. + + IF iv_lang_pair <> 'STD' OR iv_lang_pair <> 'RARE'. + RAISE invalid_lang_pair. + ENDIF. + + IF iv_word_count < 1. + RAISE invalid_word_count. + ENDIF. + + DATA(lo_pricer) = NEW {{p}}trans_pricer( ). + ev_fee = lo_pricer->calculate_fee( + iv_lang_pair = iv_lang_pair + iv_word_count = iv_word_count + iv_express = iv_express + iv_certified = iv_certified ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1115/faulty/m4_trans_pricer.clas.abap b/tasks_gen/train/G1115/faulty/m4_trans_pricer.clas.abap new file mode 100644 index 0000000..ace0179 --- /dev/null +++ b/tasks_gen/train/G1115/faulty/m4_trans_pricer.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}trans_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. + METHODS calculate_fee + IMPORTING iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + + PRIVATE SECTION. + CONSTANTS c_rate_std TYPE decfloat34 VALUE '0.12'. + CONSTANTS c_rate_rare TYPE decfloat34 VALUE '0.19'. + CONSTANTS c_min_charge TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_certified_fee TYPE decfloat34 VALUE '22.00'. + CONSTANTS c_express_percent TYPE decfloat34 VALUE '150'. + CONSTANTS c_tier_one_words TYPE i VALUE 1000. + CONSTANTS c_tier_two_words TYPE i VALUE 5000. + CONSTANTS c_tier_one_percent TYPE decfloat34 VALUE '10'. + CONSTANTS c_tier_two_percent TYPE decfloat34 VALUE '15'. + + METHODS rate + IMPORTING iv_lang_pair TYPE string + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS translation_charge + IMPORTING iv_word_count TYPE i + iv_rate TYPE decfloat34 + RETURNING VALUE(rv_charge) TYPE decfloat34. + METHODS discount + IMPORTING iv_word_count TYPE i + iv_charge TYPE decfloat34 + RETURNING VALUE(rv_discount) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}trans_pricer IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_fee. + DATA(lv_amount) = translation_charge( + iv_word_count = iv_word_count + iv_rate = rate( iv_lang_pair ) ). + lv_amount = lv_amount - discount( iv_word_count = iv_word_count + iv_charge = lv_amount ). + IF iv_certified = abap_true. + lv_amount = lv_amount + c_certified_fee. + ENDIF. + IF iv_express = abap_true. + lv_amount = round( val = lv_amount * c_express_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + rv_fee = lv_amount. + ENDMETHOD. + + METHOD rate. + CASE iv_lang_pair. + WHEN 'STD'. + rv_rate = c_rate_std. + WHEN 'RARE'. + rv_rate = c_rate_rare. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD translation_charge. + rv_charge = round( val = iv_word_count * iv_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD discount. + IF iv_word_count >= c_tier_two_words. + rv_discount = round( val = iv_charge / c_tier_two_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ELSEIF iv_word_count >= c_tier_one_words. + rv_discount = round( val = iv_charge * c_tier_one_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/generation.json b/tasks_gen/train/G1115/generation.json new file mode 100644 index 0000000..ec420ee --- /dev/null +++ b/tasks_gen/train/G1115/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1115", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0151 (similarity spec 0.60, rules 0.48, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1115/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1115/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..9cc14a4 --- /dev/null +++ b/tasks_gen/train/G1115/hidden/t14_hidden.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS call_fee + IMPORTING iv_pair TYPE string + iv_words TYPE i + iv_express TYPE abap_bool DEFAULT abap_false + iv_cert TYPE abap_bool DEFAULT abap_false + EXPORTING ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS pricer_fee + IMPORTING iv_pair TYPE string + iv_words TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34. + + METHODS minimum_charge FOR TESTING. + METHODS rare_rate FOR TESTING. + METHODS tier_one_discount FOR TESTING. + METHODS tier_two_discount FOR TESTING. + METHODS no_discount_below_tier FOR TESTING. + METHODS certified_fee FOR TESTING. + METHODS express_multiplier FOR TESTING. + METHODS discount_rounding FOR TESTING. + METHODS express_rounding FOR TESTING. + METHODS invalid_pair_first FOR TESTING. + METHODS invalid_word_count FOR TESTING. + METHODS pricer_class_fee FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD call_fee. + ev_fee = 0. + CALL FUNCTION '{{P}}TRANS_FEE' + EXPORTING + iv_lang_pair = iv_pair + iv_word_count = iv_words + iv_express = iv_express + iv_certified = iv_cert + IMPORTING + ev_fee = ev_fee + EXCEPTIONS + invalid_lang_pair = 1 + invalid_word_count = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD pricer_fee. + DATA(lo_pricer) = NEW {{p}}trans_pricer( ). + rv_fee = lo_pricer->calculate_fee( + iv_lang_pair = iv_pair + iv_word_count = iv_words + iv_express = abap_false + iv_certified = abap_false ). + ENDMETHOD. + + METHOD minimum_charge. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 100 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD rare_rate. + call_fee( EXPORTING iv_pair = 'RARE' iv_words = 200 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '38.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD tier_one_discount. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 1000 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '108.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD tier_two_discount. + call_fee( EXPORTING iv_pair = 'RARE' iv_words = 5000 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '807.50' ) act = lv_fee ). + ENDMETHOD. + + METHOD no_discount_below_tier. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 999 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '119.88' ) act = lv_fee ). + ENDMETHOD. + + METHOD certified_fee. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 300 iv_cert = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '58.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD express_multiplier. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 300 iv_cert = abap_true iv_express = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '87.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD discount_rounding. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 1001 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '108.11' ) act = lv_fee ). + ENDMETHOD. + + METHOD express_rounding. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 1001 iv_express = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '162.17' ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_pair_first. + call_fee( EXPORTING iv_pair = 'XX' iv_words = 0 + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD invalid_word_count. + call_fee( EXPORTING iv_pair = 'STD' iv_words = 0 + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. + + METHOD pricer_class_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '807.50' ) + act = pricer_fee( iv_pair = 'RARE' iv_words = 5000 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/mutation.json b/tasks_gen/train/G1115/mutation.json new file mode 100644 index 0000000..dc0a355 --- /dev/null +++ b/tasks_gen/train/G1115/mutation.json @@ -0,0 +1,90 @@ +{ + "task": "G1115", + "mutants": [ + { + "object": "{{P}}TRANS_PRICER", + "mutant": "line 50: = -> <> (eq)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "CERTIFIED_FEE", + "DISCOUNT_ROUNDING", + "EXPRESS_MULTIPLIER", + "EXPRESS_ROUNDING", + "MINIMUM_CHARGE", + "NO_DISCOUNT_BELOW_TIER", + "PRICER_CLASS_FEE", + "RARE_RATE", + "TIER_ONE_DISCOUNT", + "TIER_TWO_DISCOUNT" + ] + }, + { + "object": "{{P}}TRANS_FEE", + "mutant": "line 13: <> -> = (rel)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "CERTIFIED_FEE", + "DISCOUNT_ROUNDING", + "EXPRESS_MULTIPLIER", + "EXPRESS_ROUNDING", + "INVALID_PAIR_FIRST", + "INVALID_WORD_COUNT", + "MINIMUM_CHARGE", + "NO_DISCOUNT_BELOW_TIER", + "TIER_ONE_DISCOUNT" + ] + }, + { + "object": "{{P}}TRANS_PRICER", + "mutant": "line 53: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "CERTIFIED_FEE", + "DISCOUNT_ROUNDING", + "EXPRESS_MULTIPLIER", + "EXPRESS_ROUNDING", + "MINIMUM_CHARGE", + "NO_DISCOUNT_BELOW_TIER", + "PRICER_CLASS_FEE", + "RARE_RATE", + "TIER_ONE_DISCOUNT", + "TIER_TWO_DISCOUNT" + ] + }, + { + "object": "{{P}}TRANS_FEE", + "mutant": "line 13: AND -> OR (logic)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "CERTIFIED_FEE", + "DISCOUNT_ROUNDING", + "EXPRESS_MULTIPLIER", + "EXPRESS_ROUNDING", + "INVALID_WORD_COUNT", + "MINIMUM_CHARGE", + "NO_DISCOUNT_BELOW_TIER", + "RARE_RATE", + "TIER_ONE_DISCOUNT", + "TIER_TWO_DISCOUNT" + ] + }, + { + "object": "{{P}}TRANS_PRICER", + "mutant": "line 83: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "PRICER_CLASS_FEE", + "TIER_TWO_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1115/reference/t14_test.clas.abap b/tasks_gen/train/G1115/reference/t14_test.clas.abap new file mode 100644 index 0000000..8aff505 --- /dev/null +++ b/tasks_gen/train/G1115/reference/t14_test.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_pair TYPE string + iv_words TYPE i + iv_express TYPE abap_bool + iv_cert TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + + METHODS minimum_charge FOR TESTING. + METHODS tier_discount FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}TRANS_FEE' + EXPORTING + iv_lang_pair = iv_pair + iv_word_count = iv_words + iv_express = iv_express + iv_certified = iv_cert + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_lang_pair = 1 + invalid_word_count = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '35.00' ) + act = fee_for( iv_pair = 'STD' iv_words = 100 + iv_express = abap_false iv_cert = abap_false ) ). + ENDMETHOD. + + METHOD tier_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '108.00' ) + act = fee_for( iv_pair = 'STD' iv_words = 1000 + iv_express = abap_false iv_cert = abap_false ) ). + ENDMETHOD. + + METHOD express_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '87.00' ) + act = fee_for( iv_pair = 'STD' iv_words = 300 + iv_express = abap_true iv_cert = abap_true ) ). + ENDMETHOD. + + METHOD invalid_input. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}TRANS_FEE' + EXPORTING + iv_lang_pair = 'STD' + iv_word_count = 0 + iv_express = abap_false + iv_certified = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_lang_pair = 1 + invalid_word_count = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_equals( exp = 2 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/reference/trans_fee.func.abap b/tasks_gen/train/G1115/reference/trans_fee.func.abap new file mode 100644 index 0000000..02fe635 --- /dev/null +++ b/tasks_gen/train/G1115/reference/trans_fee.func.abap @@ -0,0 +1,28 @@ +FUNCTION {{p}}trans_fee + IMPORTING + iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_lang_pair + invalid_word_count. + + IF iv_lang_pair <> 'STD' AND iv_lang_pair <> 'RARE'. + RAISE invalid_lang_pair. + ENDIF. + + IF iv_word_count < 1. + RAISE invalid_word_count. + ENDIF. + + DATA(lo_pricer) = NEW {{p}}trans_pricer( ). + ev_fee = lo_pricer->calculate_fee( + iv_lang_pair = iv_lang_pair + iv_word_count = iv_word_count + iv_express = iv_express + iv_certified = iv_certified ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1115/reference/trans_pricer.clas.abap b/tasks_gen/train/G1115/reference/trans_pricer.clas.abap new file mode 100644 index 0000000..e7776eb --- /dev/null +++ b/tasks_gen/train/G1115/reference/trans_pricer.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}trans_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + METHODS constructor. + METHODS calculate_fee + IMPORTING iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + + PRIVATE SECTION. + CONSTANTS c_rate_std TYPE decfloat34 VALUE '0.12'. + CONSTANTS c_rate_rare TYPE decfloat34 VALUE '0.19'. + CONSTANTS c_min_charge TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_certified_fee TYPE decfloat34 VALUE '22.00'. + CONSTANTS c_express_percent TYPE decfloat34 VALUE '150'. + CONSTANTS c_tier_one_words TYPE i VALUE 1000. + CONSTANTS c_tier_two_words TYPE i VALUE 5000. + CONSTANTS c_tier_one_percent TYPE decfloat34 VALUE '10'. + CONSTANTS c_tier_two_percent TYPE decfloat34 VALUE '15'. + + METHODS rate + IMPORTING iv_lang_pair TYPE string + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS translation_charge + IMPORTING iv_word_count TYPE i + iv_rate TYPE decfloat34 + RETURNING VALUE(rv_charge) TYPE decfloat34. + METHODS discount + IMPORTING iv_word_count TYPE i + iv_charge TYPE decfloat34 + RETURNING VALUE(rv_discount) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}trans_pricer IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_fee. + DATA(lv_amount) = translation_charge( + iv_word_count = iv_word_count + iv_rate = rate( iv_lang_pair ) ). + lv_amount = lv_amount - discount( iv_word_count = iv_word_count + iv_charge = lv_amount ). + IF iv_certified = abap_true. + lv_amount = lv_amount + c_certified_fee. + ENDIF. + IF iv_express = abap_true. + lv_amount = round( val = lv_amount * c_express_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + rv_fee = lv_amount. + ENDMETHOD. + + METHOD rate. + CASE iv_lang_pair. + WHEN 'STD'. + rv_rate = c_rate_std. + WHEN 'RARE'. + rv_rate = c_rate_rare. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD translation_charge. + rv_charge = round( val = iv_word_count * iv_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD discount. + IF iv_word_count >= c_tier_two_words. + rv_discount = round( val = iv_charge * c_tier_two_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ELSEIF iv_word_count >= c_tier_one_words. + rv_discount = round( val = iv_charge * c_tier_one_percent / 100 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/reference/trans_pricer.testclasses.abap b/tasks_gen/train/G1115/reference/trans_pricer.testclasses.abap new file mode 100644 index 0000000..b817da4 --- /dev/null +++ b/tasks_gen/train/G1115/reference/trans_pricer.testclasses.abap @@ -0,0 +1,40 @@ +CLASS ltc_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}trans_pricer. + + METHODS setup. + METHODS minimum_charge FOR TESTING. + METHODS tier_discount FOR TESTING. + METHODS certified_and_express FOR TESTING. +ENDCLASS. + + +CLASS ltc_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}trans_pricer( ). + ENDMETHOD. + + METHOD minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '35.00' ) + act = mo_cut->calculate_fee( iv_lang_pair = 'STD' iv_word_count = 100 + iv_express = abap_false iv_certified = abap_false ) ). + ENDMETHOD. + + METHOD tier_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '807.50' ) + act = mo_cut->calculate_fee( iv_lang_pair = 'RARE' iv_word_count = 5000 + iv_express = abap_false iv_certified = abap_false ) ). + ENDMETHOD. + + METHOD certified_and_express. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '87.00' ) + act = mo_cut->calculate_fee( iv_lang_pair = 'STD' iv_word_count = 300 + iv_express = abap_true iv_certified = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1115/seed/trans_sample.func.abap b/tasks_gen/train/G1115/seed/trans_sample.func.abap new file mode 100644 index 0000000..20e8702 --- /dev/null +++ b/tasks_gen/train/G1115/seed/trans_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}trans_sample + IMPORTING + iv_lang_pair TYPE string + iv_word_count TYPE i + iv_express TYPE abap_bool + iv_certified TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_lang_pair + invalid_word_count. + + ev_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1115/spec.md b/tasks_gen/train/G1115/spec.md new file mode 100644 index 0000000..414b292 --- /dev/null +++ b/tasks_gen/train/G1115/spec.md @@ -0,0 +1,86 @@ +# 1. Goal + +A translation agency prices translation jobs. The order desk calls one function +module for each job. The function module must have the same interface as the +sample module {{P}}TRANS_SAMPLE, because the order desk calls all pricing modules +of the agency in the same way. Other programs of the agency also need the price. +Therefore the calculation is available in a separate class. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_TRANS exists in package $TMP. +- Function module {{P}}TRANS_SAMPLE exists in this group. It is the sample + pricing module. Its interface is the interface that all pricing modules of the + agency use. + +# 4. Contract + +## 4.1 Function module {{P}}TRANS_FEE + +- Function group: {{P}}FG_TRANS. +- Interface: exactly the interface of {{P}}TRANS_SAMPLE. + - Importing parameters: + - iv_lang_pair TYPE string + - iv_word_count TYPE i + - iv_express TYPE abap_bool + - iv_certified TYPE abap_bool + - Exporting parameter: ev_fee TYPE decfloat34 + - Exceptions: invalid_lang_pair, invalid_word_count + +## 4.2 Class {{P}}TRANS_PRICER + +- Package $TMP. +- Public and final. +- Public constructor without parameters. +- Public instance method calculate_fee: + - Importing parameters: + - iv_lang_pair TYPE string + - iv_word_count TYPE i + - iv_express TYPE abap_bool + - iv_certified TYPE abap_bool + - Returning parameter: rv_fee TYPE decfloat34 + - The method has no exceptions. It expects valid input. + +# 5. Business rules + +1. The language pair is STD or RARE. Any other value is invalid. +2. The word count is 1 or more. A word count of 0 or a negative word count is + invalid. +3. The function module {{P}}TRANS_FEE checks the language pair first and the + word count second. It raises the exception INVALID_LANG_PAIR for an invalid + language pair and the exception INVALID_WORD_COUNT for an invalid word count. +4. The price of one word is 0.12 for language pair STD and 0.19 for language + pair RARE. +5. The translation charge is the word count multiplied by the price of one + word. Round the translation charge half up to 2 decimals. +6. The minimum translation charge is 35.00. If the translation charge is less + than 35.00, the translation charge is 35.00. +7. The agency grants a volume discount on the translation charge after rule 6: + - 10 % for 1000 words up to 4999 words, + - 15 % for 5000 words or more. + Round the discount half up to 2 decimals. The discounted charge is the + translation charge minus the discount. +8. A certified translation adds a flat fee of 22.00 to the discounted charge. +9. Express delivery multiplies the amount after rules 7 and 8 by 1.5. Round the + result half up to 2 decimals. +10. Without express delivery, the fee is the amount after rules 7 and 8. +11. The class {{P}}TRANS_PRICER returns the fee in rv_fee. The function module + {{P}}TRANS_FEE returns the fee in ev_fee. + +# 6. Constraints + +- Release target: 8.16 (SAP_BASIS 816). +- Package $TMP. Do not use a transport. +- Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}TRANS_SAMPLE. + +# 7. Acceptance + +- All objects are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests: local test classes for {{P}}TRANS_PRICER and a global + test class for {{P}}TRANS_FEE. diff --git a/tasks_gen/train/G1115/task.json b/tasks_gen/train/G1115/task.json new file mode 100644 index 0000000..08b1fec --- /dev/null +++ b/tasks_gen/train/G1115/task.json @@ -0,0 +1,95 @@ +{ + "id": "G1115", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_TRANS", + "description": "Translation job pricing function group" + }, + { + "type": "FUNC", + "name": "{{P}}TRANS_SAMPLE", + "functionGroup": "{{P}}FG_TRANS", + "file": "seed/trans_sample.func.abap", + "description": "Sample pricing module of the translation agency" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}TRANS_FEE", + "functionGroup": "{{P}}FG_TRANS", + "params": [ + { + "name": "iv_lang_pair", + "type": "string" + }, + { + "name": "iv_word_count", + "type": "i" + }, + { + "name": "iv_express", + "type": "abap_bool" + }, + { + "name": "iv_certified", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + }, + { + "type": "CLAS", + "name": "{{P}}TRANS_PRICER" + } + ], + "out_of_scope": [ + "{{P}}TRANS_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TRANS_PRICER", + "file": "reference/trans_pricer.clas.abap", + "testclasses_file": "reference/trans_pricer.testclasses.abap", + "description": "Pure translation price calculation" + }, + { + "type": "FUNC", + "name": "{{P}}TRANS_FEE", + "functionGroup": "{{P}}FG_TRANS", + "file": "reference/trans_fee.func.abap", + "description": "Translation fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests for the function module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1116/faulty/m0_carpet_fee.func.abap b/tasks_gen/train/G1116/faulty/m0_carpet_fee.func.abap new file mode 100644 index 0000000..252b088 --- /dev/null +++ b/tasks_gen/train/G1116/faulty/m0_carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm > 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true AND iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type = 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_true. + lv_fee = lv_fee + 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/faulty/m1_carpet_fee.func.abap b/tasks_gen/train/G1116/faulty/m1_carpet_fee.func.abap new file mode 100644 index 0000000..65c34dc --- /dev/null +++ b/tasks_gen/train/G1116/faulty/m1_carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm <= 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true OR iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type = 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_true. + lv_fee = lv_fee + 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/faulty/m2_carpet_fee.func.abap b/tasks_gen/train/G1116/faulty/m2_carpet_fee.func.abap new file mode 100644 index 0000000..c57e73c --- /dev/null +++ b/tasks_gen/train/G1116/faulty/m2_carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm <= 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true AND iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type <> 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_true. + lv_fee = lv_fee + 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/faulty/m3_carpet_fee.func.abap b/tasks_gen/train/G1116/faulty/m3_carpet_fee.func.abap new file mode 100644 index 0000000..fbb4c3c --- /dev/null +++ b/tasks_gen/train/G1116/faulty/m3_carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm <= 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true AND iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type = 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_false. + lv_fee = lv_fee + 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/faulty/m4_carpet_fee.func.abap b/tasks_gen/train/G1116/faulty/m4_carpet_fee.func.abap new file mode 100644 index 0000000..9101c39 --- /dev/null +++ b/tasks_gen/train/G1116/faulty/m4_carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm <= 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true AND iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type = 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_true. + lv_fee = lv_fee - 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/generation.json b/tasks_gen/train/G1116/generation.json new file mode 100644 index 0000000..be5116b --- /dev/null +++ b/tasks_gen/train/G1116/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1116", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1116/hidden/t42_hidden.clas.abap b/tasks_gen/train/G1116/hidden/t42_hidden.clas.abap new file mode 100644 index 0000000..fa59900 --- /dev/null +++ b/tasks_gen/train/G1116/hidden/t42_hidden.clas.abap @@ -0,0 +1,134 @@ +CLASS {{p}}t42_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_type TYPE string + iv_area TYPE decfloat34 + iv_stain TYPE abap_bool DEFAULT abap_false + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + ev_subrc TYPE sysubrc. + METHODS carpet_rate FOR TESTING. + METHODS upholstery_stain FOR TESTING. + METHODS rug_rate FOR TESTING. + METHODS minimum_fee_note FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS invalid_type FOR TESTING. + METHODS invalid_area FOR TESTING. + METHODS stain_not_allowed FOR TESTING. + METHODS area_too_large FOR TESTING. + METHODS type_checked_first FOR TESTING. + METHODS area_before_stain FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_hidden IMPLEMENTATION. + METHOD call. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = iv_type + iv_area_sqm = iv_area + iv_stain = iv_stain + IMPORTING + ev_fee = ev_fee + ev_note = ev_note + EXCEPTIONS + invalid_type = 1 + invalid_area = 2 + stain_not_allowed = 3 + area_too_large = 4 + OTHERS = 5. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD carpet_rate. + call( EXPORTING iv_type = `CP` iv_area = 20 + IMPORTING ev_fee = DATA(lv_fee) ev_note = DATA(lv_note) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '70.00' ) act = lv_fee ). + cl_abap_unit_assert=>assert_initial( act = lv_note ). + + call( EXPORTING iv_type = `CP` iv_area = 100 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '350.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD upholstery_stain. + call( EXPORTING iv_type = `UP` iv_area = 10 iv_stain = abap_true + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '75.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD rug_rate. + call( EXPORTING iv_type = `RU` iv_area = 25 + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '50.00' ) act = lv_fee ). + + call( EXPORTING iv_type = `RU` iv_area = 200 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '400.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee_note. + call( EXPORTING iv_type = `CP` iv_area = 5 + IMPORTING ev_fee = DATA(lv_fee) ev_note = DATA(lv_note) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '40.00' ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = `MINIMUM FEE APPLIED` act = lv_note ). + + call( EXPORTING iv_type = `UP` iv_area = 8 + IMPORTING ev_fee = lv_fee ev_note = lv_note ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '40.00' ) act = lv_fee ). + cl_abap_unit_assert=>assert_initial( act = lv_note ). + ENDMETHOD. + + METHOD rounding_half_up. + call( EXPORTING iv_type = `CP` iv_area = '12.57' + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '44.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_type. + call( EXPORTING iv_type = `XX` iv_area = 10 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD invalid_area. + call( EXPORTING iv_type = `CP` iv_area = 0 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. + + METHOD stain_not_allowed. + call( EXPORTING iv_type = `RU` iv_area = 10 iv_stain = abap_true + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rc ). + ENDMETHOD. + + METHOD area_too_large. + call( EXPORTING iv_type = `CP` iv_area = '100.01' + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rc ). + ENDMETHOD. + + METHOD type_checked_first. + call( EXPORTING iv_type = `XX` iv_area = -5 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD area_before_stain. + call( EXPORTING iv_type = `RU` iv_area = -5 iv_stain = abap_true + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1116/mutation.json b/tasks_gen/train/G1116/mutation.json new file mode 100644 index 0000000..0648e64 --- /dev/null +++ b/tasks_gen/train/G1116/mutation.json @@ -0,0 +1,68 @@ +{ + "task": "G1116", + "mutants": [ + { + "object": "{{P}}CARPET_FEE", + "mutant": "line 19: <= -> > (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "AREA_BEFORE_STAIN", + "AREA_TOO_LARGE", + "CARPET_RATE", + "INVALID_AREA", + "MINIMUM_FEE_NOTE", + "ROUNDING_HALF_UP", + "RUG_RATE", + "STAIN_NOT_ALLOWED", + "UPHOLSTERY_STAIN" + ] + }, + { + "object": "{{P}}CARPET_FEE", + "mutant": "line 23: AND -> OR (logic)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "RUG_RATE", + "UPHOLSTERY_STAIN" + ] + }, + { + "object": "{{P}}CARPET_FEE", + "mutant": "line 27: = -> <> (eq)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "AREA_TOO_LARGE", + "RUG_RATE" + ] + }, + { + "object": "{{P}}CARPET_FEE", + "mutant": "line 42: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "CARPET_RATE", + "MINIMUM_FEE_NOTE", + "ROUNDING_HALF_UP", + "RUG_RATE", + "UPHOLSTERY_STAIN" + ] + }, + { + "object": "{{P}}CARPET_FEE", + "mutant": "line 43: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "UPHOLSTERY_STAIN" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1116/reference/carpet_fee.func.abap b/tasks_gen/train/G1116/reference/carpet_fee.func.abap new file mode 100644 index 0000000..be542bd --- /dev/null +++ b/tasks_gen/train/G1116/reference/carpet_fee.func.abap @@ -0,0 +1,55 @@ +FUNCTION {{p}}carpet_fee + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + IF iv_clean_type <> 'CP' AND iv_clean_type <> 'UP' AND iv_clean_type <> 'RU'. + RAISE invalid_type. + ENDIF. + + IF iv_area_sqm <= 0. + RAISE invalid_area. + ENDIF. + + IF iv_stain = abap_true AND iv_clean_type = 'RU'. + RAISE stain_not_allowed. + ENDIF. + + IF iv_clean_type = 'CP' AND iv_area_sqm > 100. + RAISE area_too_large. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_clean_type. + WHEN 'CP'. + lv_rate = '3.50'. + WHEN 'UP'. + lv_rate = '5.00'. + WHEN OTHERS. + lv_rate = '2.00'. + ENDCASE. + + DATA(lv_fee) = iv_area_sqm * lv_rate. + IF iv_stain = abap_true. + lv_fee = lv_fee + 25. + ENDIF. + lv_fee = round( val = lv_fee dec = 2 mode = cl_abap_math=>round_half_up ). + + IF lv_fee < 40. + ev_fee = 40. + ev_note = 'MINIMUM FEE APPLIED'. + ELSE. + ev_fee = lv_fee. + ev_note = ''. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/reference/t42_test.clas.abap b/tasks_gen/train/G1116/reference/t42_test.clas.abap new file mode 100644 index 0000000..288df2b --- /dev/null +++ b/tasks_gen/train/G1116/reference/t42_test.clas.abap @@ -0,0 +1,114 @@ +CLASS {{p}}t42_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_type TYPE string + iv_area TYPE decfloat34 + iv_stain TYPE abap_bool DEFAULT abap_false + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS carpet_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS stain_surcharge FOR TESTING. + METHODS invalid_type FOR TESTING. + METHODS invalid_area FOR TESTING. + METHODS stain_not_allowed FOR TESTING. + METHODS area_too_large FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_note TYPE string. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = iv_type + iv_area_sqm = iv_area + iv_stain = iv_stain + IMPORTING + ev_fee = rv_fee + ev_note = lv_note + EXCEPTIONS + OTHERS = 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD carpet_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '70.00' ) + act = fee_for( iv_type = `CP` iv_area = 20 ) ). + ENDMETHOD. + + METHOD minimum_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '40.00' ) + act = fee_for( iv_type = `CP` iv_area = 5 ) ). + ENDMETHOD. + + METHOD stain_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '75.00' ) + act = fee_for( iv_type = `UP` iv_area = 10 iv_stain = abap_true ) ). + ENDMETHOD. + + METHOD invalid_type. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = `XX` + iv_area_sqm = 10 + iv_stain = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_type = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. + + METHOD invalid_area. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = `CP` + iv_area_sqm = 0 + iv_stain = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_area = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_equals( exp = 2 act = sy-subrc ). + ENDMETHOD. + + METHOD stain_not_allowed. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = `RU` + iv_area_sqm = 10 + iv_stain = abap_true + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + stain_not_allowed = 3 + OTHERS = 4. + cl_abap_unit_assert=>assert_equals( exp = 3 act = sy-subrc ). + ENDMETHOD. + + METHOD area_too_large. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}CARPET_FEE' + EXPORTING + iv_clean_type = `CP` + iv_area_sqm = '120.5' + iv_stain = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + area_too_large = 4 + OTHERS = 5. + cl_abap_unit_assert=>assert_equals( exp = 4 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1116/seed/carpet_sample.func.abap b/tasks_gen/train/G1116/seed/carpet_sample.func.abap new file mode 100644 index 0000000..9828f9f --- /dev/null +++ b/tasks_gen/train/G1116/seed/carpet_sample.func.abap @@ -0,0 +1,20 @@ +FUNCTION {{p}}carpet_sample + IMPORTING + iv_clean_type TYPE string + iv_area_sqm TYPE decfloat34 + iv_stain TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_note TYPE string + EXCEPTIONS + invalid_type + invalid_area + stain_not_allowed + area_too_large. + + " Sample module. It shows the interface that the pricing modules of the + " group use. The calculation of the fee is not part of this sample. + ev_fee = 0. + ev_note = ''. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1116/spec.md b/tasks_gen/train/G1116/spec.md new file mode 100644 index 0000000..1cd511e --- /dev/null +++ b/tasks_gen/train/G1116/spec.md @@ -0,0 +1,62 @@ +# 1. Goal +Calculate the fee of a cleaning job for a carpet cleaning company. The order +desk calls one function module for each cleaning job. The function module +checks the job data, calculates the fee, and returns a note for the order desk. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_CARPET exists in package $TMP. +- Function module {{P}}CARPET_SAMPLE exists in this group. It shows the + interface that the pricing modules of the group use. +- The order desk calls the pricing module for each cleaning job. + +# 4. Contract +Create the function module {{P}}CARPET_FEE in function group {{P}}FG_CARPET. +Use exactly the interface of {{P}}CARPET_SAMPLE: +- Importing parameters: + - iv_clean_type TYPE string + - iv_area_sqm TYPE decfloat34 + - iv_stain TYPE abap_bool +- Exporting parameters: + - ev_fee TYPE decfloat34 + - ev_note TYPE string +- Exceptions: + - invalid_type + - invalid_area + - stain_not_allowed + - area_too_large + +# 5. Business rules +1. Check the job data in this order: cleaning type, area, stain option, carpet + size limit. Raise only the first exception that applies. +2. The cleaning type is 'CP' (carpet), 'UP' (upholstery) or 'RU' (rug). For + any other value, raise invalid_type. +3. The area must be greater than 0. For an area of 0 or less, raise + invalid_area. +4. A stain treatment is possible for 'CP' and 'UP' only. If iv_stain is true + and the cleaning type is 'RU', raise stain_not_allowed. +5. A carpet with an area greater than 100 square metres needs a site visit. If + the cleaning type is 'CP' and the area is greater than 100, raise + area_too_large. +6. The base rate for each square metre is 3.50 for 'CP', 5.00 for 'UP' and 2.00 + for 'RU'. +7. A stain treatment adds 25.00 to the fee. +8. The fee is the base amount plus the surcharge, rounded half up to 2 decimal + places. +9. The minimum fee is 40.00. If the fee from rule 8 is less than 40.00, the fee + is 40.00 and the note is 'MINIMUM FEE APPLIED'. In all other cases the note + is empty. +10. If an exception is raised, the fee and the note are not set. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Keep the function module short and readable. +- Out of scope: do not change {{P}}CARPET_SAMPLE. Do not create database + tables. + +# 7. Acceptance +- The function module {{P}}CARPET_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1116/task.json b/tasks_gen/train/G1116/task.json new file mode 100644 index 0000000..0368446 --- /dev/null +++ b/tasks_gen/train/G1116/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1116", + "category": "D", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_CARPET", + "description": "Carpet cleaning pricing" + }, + { + "type": "FUNC", + "name": "{{P}}CARPET_SAMPLE", + "functionGroup": "{{P}}FG_CARPET", + "file": "seed/carpet_sample.func.abap", + "description": "Sample pricing module that shows the interface of the group" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}CARPET_FEE", + "functionGroup": "{{P}}FG_CARPET", + "params": [ + { + "name": "iv_clean_type", + "type": "string" + }, + { + "name": "iv_area_sqm", + "type": "decfloat34" + }, + { + "name": "iv_stain", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + }, + { + "name": "ev_note", + "type": "string" + } + ] + } + ], + "out_of_scope": [ + "{{P}}CARPET_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T42_HIDDEN", + "file": "hidden/t42_hidden.clas.abap", + "description": "T42 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}CARPET_FEE", + "functionGroup": "{{P}}FG_CARPET", + "file": "reference/carpet_fee.func.abap", + "description": "Carpet cleaning fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T42_TEST", + "file": "reference/t42_test.clas.abap", + "description": "T42 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1117/generation.json b/tasks_gen/train/G1117/generation.json new file mode 100644 index 0000000..fcbdc99 --- /dev/null +++ b/tasks_gen/train/G1117/generation.json @@ -0,0 +1,44 @@ +{ + "id": "G1117", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.72, rules 0.64, names 0.33). Choose a different business topic and different object names.", + "Too close to task T13 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.69, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task T13 (similarity spec 0.69, rules 0.61, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t21_hidden.clas.abap line 110: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1117/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1117/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..2f9138a --- /dev/null +++ b/tasks_gen/train/G1117/hidden/t21_hidden.clas.abap @@ -0,0 +1,117 @@ +CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_postcode TYPE string + iv_weight TYPE decfloat34 + iv_express TYPE abap_bool DEFAULT abap_false + EXPORTING ev_tour TYPE string + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS standard_fee FOR TESTING. + METHODS weight_surcharge FOR TESTING. + METHODS started_kilogram FOR TESTING. + METHODS heavy_tour FOR TESTING. + METHODS tour_boundary FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS fee_cap FOR TESTING. + METHODS island_skip FOR TESTING. + METHODS unknown_zone FOR TESTING. + METHODS invalid_weight FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD call. + CALL FUNCTION '{{P}}PARCEL_FEE' + EXPORTING + iv_postcode = iv_postcode + iv_weight = iv_weight + iv_express = iv_express + IMPORTING + ev_tour = ev_tour + ev_skip = ev_skip + ev_fee = ev_fee + EXCEPTIONS + unknown_zone = 1 + invalid_weight = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD standard_fee. + call( EXPORTING iv_postcode = `10115` iv_weight = 1 + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `N` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.90' ) act = lv_fee ). + ENDMETHOD. + + METHOD weight_surcharge. + call( EXPORTING iv_postcode = `20095` iv_weight = 3 + IMPORTING ev_tour = DATA(lv_tour) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `W` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '6.20' ) act = lv_fee ). + ENDMETHOD. + + METHOD started_kilogram. + call( EXPORTING iv_postcode = `40213` iv_weight = '3.01' + IMPORTING ev_tour = DATA(lv_tour) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `C` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '7.25' ) act = lv_fee ). + ENDMETHOD. + + METHOD heavy_tour. + call( EXPORTING iv_postcode = `60311` iv_weight = '10.01' + IMPORTING ev_tour = DATA(lv_tour) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `EH` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.60' ) act = lv_fee ). + ENDMETHOD. + + METHOD tour_boundary. + call( EXPORTING iv_postcode = `60311` iv_weight = 10 + IMPORTING ev_tour = DATA(lv_tour) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `E` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.25' ) act = lv_fee ). + ENDMETHOD. + + METHOD express_surcharge. + call( EXPORTING iv_postcode = `10115` iv_weight = 1 iv_express = abap_true + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `N` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8.40' ) act = lv_fee ). + ENDMETHOD. + + METHOD fee_cap. + call( EXPORTING iv_postcode = `60311` iv_weight = 60 + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `EH` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_fee ). + ENDMETHOD. + + METHOD island_skip. + call( EXPORTING iv_postcode = `80331` iv_weight = 2 + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `S` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_fee ). + ENDMETHOD. + + METHOD unknown_zone. + call( EXPORTING iv_postcode = `XYZ` iv_weight = 1 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD invalid_weight. + call( EXPORTING iv_postcode = `10115` iv_weight = 0 + IMPORTING ev_subrc = DATA(lv_rc_zero) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc_zero ). + call( EXPORTING iv_postcode = `10115` iv_weight = -5 + IMPORTING ev_subrc = DATA(lv_rc_neg) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc_neg ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1117/mutation.json b/tasks_gen/train/G1117/mutation.json new file mode 100644 index 0000000..877c620 --- /dev/null +++ b/tasks_gen/train/G1117/mutation.json @@ -0,0 +1,75 @@ +{ + "task": "T21", + "mutants": [ + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 22: 4.9 -> 5.9 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "STANDARD_FEE" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 30: > -> >= (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "HEAVY_TOUR", + "TOUR_BOUNDARY" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 41: ceil -> floor", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "STARTED_KILOGRAM", + "HEAVY_TOUR", + "WEIGHT_SURCHARGE" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 47: 3.5 -> 3.6 (const)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "EXPRESS_SURCHARGE" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 51: 25 -> 26 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "FEE_CAP" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 54: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "ISLAND_SKIP" + ] + }, + { + "object": "{{P}}PARCEL_FEE", + "mutant": "line 12: <= -> < (rel)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "INVALID_WEIGHT" + ] + } + ], + "valid": 7, + "killed": 7, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1117/reference/parcel_fee.func.abap b/tasks_gen/train/G1117/reference/parcel_fee.func.abap new file mode 100644 index 0000000..9750b9c --- /dev/null +++ b/tasks_gen/train/G1117/reference/parcel_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}parcel_fee + IMPORTING + iv_postcode TYPE string + iv_weight TYPE decfloat34 + iv_express TYPE abap_bool + EXPORTING + ev_tour TYPE string + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + EXCEPTIONS + unknown_zone + invalid_weight. + + CONSTANTS lc_max_fee TYPE decfloat34 VALUE '25'. + CONSTANTS lc_express_fee TYPE decfloat34 VALUE '3.5'. + CONSTANTS lc_kg_fee TYPE decfloat34 VALUE '0.35'. + CONSTANTS lc_heavy_limit TYPE decfloat34 VALUE '10'. + + IF iv_weight <= 0. + RAISE invalid_weight. + ENDIF. + + CALL FUNCTION '{{P}}ZONE_LOOKUP' + EXPORTING + iv_postcode = iv_postcode + IMPORTING + ev_zone = DATA(lv_zone). + + IF lv_zone IS INITIAL. + RAISE unknown_zone. + ENDIF. + + ev_skip = abap_false. + ev_tour = lv_zone. + IF iv_weight > lc_heavy_limit. + ev_tour = |{ ev_tour }H|. + ENDIF. + + CASE lv_zone. + WHEN 'N'. + ev_fee = 4.9. + WHEN 'W'. + ev_fee = 5.5. + WHEN 'C'. + ev_fee = 6.2. + WHEN 'E'. + ev_fee = 7.1. + WHEN 'S'. + ev_fee = 8.4. + ENDCASE. + + DATA(lv_started_kg) = ceil( iv_weight ). + DATA(lv_extra_kg) = lv_started_kg - 1. + IF lv_extra_kg > 0. + ev_fee = ev_fee + lv_extra_kg * lc_kg_fee. + ENDIF. + + IF iv_express = abap_true. + ev_fee = ev_fee + lc_express_fee. + ENDIF. + + IF ev_fee > lc_max_fee. + ev_fee = lc_max_fee. + ENDIF. + + IF lv_zone = 'S' AND iv_express = abap_false. + ev_skip = abap_true. + ev_fee = 0. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1117/reference/t21_test.clas.abap b/tasks_gen/train/G1117/reference/t21_test.clas.abap new file mode 100644 index 0000000..e2b569c --- /dev/null +++ b/tasks_gen/train/G1117/reference/t21_test.clas.abap @@ -0,0 +1,80 @@ +CLASS {{p}}t21_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_postcode TYPE string + iv_weight TYPE decfloat34 + iv_express TYPE abap_bool DEFAULT abap_false + EXPORTING ev_tour TYPE string + ev_skip TYPE abap_bool + ev_fee TYPE decfloat34. + METHODS standard_parcel FOR TESTING. + METHODS heavy_parcel FOR TESTING. + METHODS express_parcel FOR TESTING. + METHODS island_parcel FOR TESTING. + METHODS unknown_zone FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}PARCEL_FEE' + EXPORTING + iv_postcode = iv_postcode + iv_weight = iv_weight + iv_express = iv_express + IMPORTING + ev_tour = ev_tour + ev_skip = ev_skip + ev_fee = ev_fee + EXCEPTIONS + unknown_zone = 1 + invalid_weight = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD standard_parcel. + fee_for( EXPORTING iv_postcode = `20095` iv_weight = 3 + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `W` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '6.20' ) act = lv_fee ). + ENDMETHOD. + + METHOD heavy_parcel. + fee_for( EXPORTING iv_postcode = `60311` iv_weight = '10.01' + IMPORTING ev_tour = DATA(lv_tour) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `EH` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.60' ) act = lv_fee ). + ENDMETHOD. + + METHOD express_parcel. + fee_for( EXPORTING iv_postcode = `10115` iv_weight = 1 iv_express = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8.40' ) act = lv_fee ). + ENDMETHOD. + + METHOD island_parcel. + fee_for( EXPORTING iv_postcode = `80331` iv_weight = 2 + IMPORTING ev_tour = DATA(lv_tour) ev_skip = DATA(lv_skip) ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = `S` act = lv_tour ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_fee ). + ENDMETHOD. + + METHOD unknown_zone. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}PARCEL_FEE' + EXPORTING + iv_postcode = `XYZ` + iv_weight = 1 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + unknown_zone = 1 + invalid_weight = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1117/seed/zone_lookup.func.abap b/tasks_gen/train/G1117/seed/zone_lookup.func.abap new file mode 100644 index 0000000..183ac00 --- /dev/null +++ b/tasks_gen/train/G1117/seed/zone_lookup.func.abap @@ -0,0 +1,31 @@ +FUNCTION {{p}}zone_lookup + IMPORTING + iv_postcode TYPE string + EXPORTING + ev_zone TYPE string. + + DATA lv_first TYPE c LENGTH 1. + + CLEAR ev_zone. + IF iv_postcode IS INITIAL. + RETURN. + ENDIF. + + lv_first = substring( val = iv_postcode off = 0 len = 1 ). + + CASE lv_first. + WHEN '0' OR '1'. + ev_zone = 'N'. + WHEN '2' OR '3'. + ev_zone = 'W'. + WHEN '4' OR '5'. + ev_zone = 'C'. + WHEN '6' OR '7'. + ev_zone = 'E'. + WHEN '8' OR '9'. + ev_zone = 'S'. + WHEN OTHERS. + CLEAR ev_zone. + ENDCASE. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1117/spec.md b/tasks_gen/train/G1117/spec.md new file mode 100644 index 0000000..0a3189b --- /dev/null +++ b/tasks_gen/train/G1117/spec.md @@ -0,0 +1,58 @@ +# 1. Goal +A regional parcel delivery company plans the tours of its vans. For each parcel +the dispatch program calls one function module. The function module finds the +delivery zone of the parcel, assigns the parcel to a tour, and calculates the +transport fee. The dispatch program adds the fee to the invoice of the parcel, +except when the skip flag is true. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_PARCEL exists in package $TMP. +- Function module {{P}}ZONE_LOOKUP exists in this group. It returns the +delivery zone of a postcode. Read its interface and its source before you use +it. The zone of a postcode is the value that {{P}}ZONE_LOOKUP returns for that +postcode. +- The dispatch program calls {{P}}PARCEL_FEE for each parcel. It adds ev_fee to +the invoice of the parcel. If ev_skip is true, it does not add the fee. + +# 4. Contract +- Create the function module {{P}}PARCEL_FEE in function group {{P}}FG_PARCEL. +- Importing parameters: + - iv_postcode TYPE string + - iv_weight TYPE decfloat34 (the weight of the parcel in kilograms) + - iv_express TYPE abap_bool +- Exporting parameters: + - ev_tour TYPE string + - ev_skip TYPE abap_bool + - ev_fee TYPE decfloat34 +- Exceptions: UNKNOWN_ZONE, INVALID_WEIGHT. + +# 5. Business rules +1. If iv_weight is less than or equal to 0, raise the exception INVALID_WEIGHT. +2. Find the zone of iv_postcode with {{P}}ZONE_LOOKUP. If the zone is empty, + raise the exception UNKNOWN_ZONE. +3. Set the skip flag to false. +4. Set the tour to the zone. If iv_weight is more than 10, append the character + H to the tour. +5. The base fee of a zone is: N 4.90, W 5.50, C 6.20, E 7.10, S 8.40. +6. The weight surcharge is 0.35 for each started kilogram above the first + kilogram. A started kilogram is a kilogram that is not fully reached. + Examples: 1.00 kg gives 0.00, 1.01 kg gives 0.35, 3.00 kg gives 0.70, + 3.01 kg gives 1.05. +7. The fee is the base fee of the zone plus the weight surcharge. +8. If iv_express is true, add 3.50 to the fee. +9. The fee of one parcel is never more than 25.00. +10. A parcel of zone S is collected only as an express parcel. If the zone is S + and iv_express is false, set the skip flag to true and set the fee to 0.00. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}ZONE_LOOKUP. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1117/task.json b/tasks_gen/train/G1117/task.json new file mode 100644 index 0000000..085ef9d --- /dev/null +++ b/tasks_gen/train/G1117/task.json @@ -0,0 +1,88 @@ +{ + "id": "G1117", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_PARCEL", + "description": "Parcel dispatch function group" + }, + { + "type": "FUNC", + "name": "{{P}}ZONE_LOOKUP", + "functionGroup": "{{P}}FG_PARCEL", + "file": "seed/zone_lookup.func.abap", + "description": "Returns the delivery zone of a postcode" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}PARCEL_FEE", + "functionGroup": "{{P}}FG_PARCEL", + "params": [ + { + "name": "iv_postcode", + "type": "string" + }, + { + "name": "iv_weight", + "type": "decfloat34" + }, + { + "name": "iv_express", + "type": "abap_bool" + }, + { + "name": "ev_tour", + "type": "string" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}ZONE_LOOKUP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}PARCEL_FEE", + "functionGroup": "{{P}}FG_PARCEL", + "file": "reference/parcel_fee.func.abap", + "description": "Parcel dispatch module" + }, + { + "type": "CLAS", + "name": "{{P}}T21_TEST", + "file": "reference/t21_test.clas.abap", + "description": "T21 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1118/faulty/m0_subs_sum.prog.abap b/tasks_gen/train/G1118/faulty/m0_subs_sum.prog.abap new file mode 100644 index 0000000..4a3dd7d --- /dev/null +++ b/tasks_gen/train/G1118/faulty/m0_subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status <> 'A' + AND start_date >= iv_from + AND magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs >= 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/faulty/m1_subs_sum.prog.abap b/tasks_gen/train/G1118/faulty/m1_subs_sum.prog.abap new file mode 100644 index 0000000..d139472 --- /dev/null +++ b/tasks_gen/train/G1118/faulty/m1_subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status = 'A' + OR start_date >= iv_from + AND magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs >= 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/faulty/m2_subs_sum.prog.abap b/tasks_gen/train/G1118/faulty/m2_subs_sum.prog.abap new file mode 100644 index 0000000..d3772e1 --- /dev/null +++ b/tasks_gen/train/G1118/faulty/m2_subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status = 'A' + AND start_date < iv_from + AND magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs >= 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/faulty/m3_subs_sum.prog.abap b/tasks_gen/train/G1118/faulty/m3_subs_sum.prog.abap new file mode 100644 index 0000000..e8dc02e --- /dev/null +++ b/tasks_gen/train/G1118/faulty/m3_subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status = 'A' + AND start_date >= iv_from + OR magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs >= 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/faulty/m4_subs_sum.prog.abap b/tasks_gen/train/G1118/faulty/m4_subs_sum.prog.abap new file mode 100644 index 0000000..698d661 --- /dev/null +++ b/tasks_gen/train/G1118/faulty/m4_subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status = 'A' + AND start_date >= iv_from + AND magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs < 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/generation.json b/tasks_gen/train/G1118/generation.json new file mode 100644 index 0000000..d6eaa59 --- /dev/null +++ b/tasks_gen/train/G1118/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1118", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 95.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1118/hidden/t15_hidden.clas.abap b/tasks_gen/train/G1118/hidden/t15_hidden.clas.abap new file mode 100644 index 0000000..6e1f791 --- /dev/null +++ b/tasks_gen/train/G1118/hidden/t15_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}t15_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + METHODS run + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS two_magazines FOR TESTING. + METHODS sorted_by_revenue FOR TESTING. + METHODS totals_and_counts FOR TESTING. + METHODS cancelled_excluded FOR TESTING. + METHODS small_group_hidden FOR TESTING. + METHODS magazine_filter FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS empty_result FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t15_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}subs_sum WITH p_from = iv_from WITH s_mag IN it_magazine AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD two_magazines. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( run( '20260101' ) ) ). + ENDMETHOD. + + METHOD sorted_by_revenue. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `NATURE` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MAGAZINE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `ORBIT` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `MAGAZINE` ) ). + ENDMETHOD. + + METHOD totals_and_counts. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `40.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `NUM_SUBS` ) ). + ENDMETHOD. + + METHOD cancelled_excluded. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `20.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REVENUE` ) ). + ENDMETHOD. + + METHOD small_group_hidden. + DATA(lr_data) = run( iv_from = '20260101' + it_magazine = VALUE #( ( sign = 'I' option = 'EQ' low = 'SPORT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( lr_data ) ). + ENDMETHOD. + + METHOD magazine_filter. + DATA(lr_data) = run( iv_from = '20260101' + it_magazine = VALUE #( ( sign = 'I' option = 'EQ' low = 'NATURE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `NATURE` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MAGAZINE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `40.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( '20251201' ). + cl_abap_unit_assert=>assert_equals( exp = `45.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `NUM_SUBS` ) ). + ENDMETHOD. + + METHOD empty_result. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( run( '20260201' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/mutation.json b/tasks_gen/train/G1118/mutation.json new file mode 100644 index 0000000..fef36fc --- /dev/null +++ b/tasks_gen/train/G1118/mutation.json @@ -0,0 +1,82 @@ +{ + "task": "G1118", + "mutants": [ + { + "object": "{{P}}SUBS_SUM", + "mutant": "line 34: = -> <> (eq)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "MAGAZINE_FILTER", + "SORTED_BY_REVENUE", + "TOTALS_AND_COUNTS", + "TWO_MAGAZINES" + ] + }, + { + "object": "{{P}}SUBS_SUM", + "mutant": "line 35: AND -> OR (logic)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "EMPTY_RESULT", + "MAGAZINE_FILTER", + "SMALL_GROUP_HIDDEN", + "TOTALS_AND_COUNTS", + "TWO_MAGAZINES" + ] + }, + { + "object": "{{P}}SUBS_SUM", + "mutant": "line 35: >= -> < (rel)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "EMPTY_RESULT", + "MAGAZINE_FILTER", + "SORTED_BY_REVENUE", + "TOTALS_AND_COUNTS", + "TWO_MAGAZINES" + ] + }, + { + "object": "{{P}}SUBS_SUM", + "mutant": "line 36: AND -> OR (logic)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "EMPTY_RESULT", + "MAGAZINE_FILTER", + "SMALL_GROUP_HIDDEN", + "TOTALS_AND_COUNTS", + "TWO_MAGAZINES" + ] + }, + { + "object": "{{P}}SUBS_SUM", + "mutant": "line 43: >= -> < (rel)", + "status": "killed", + "hidden": "0/8", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "EMPTY_RESULT", + "MAGAZINE_FILTER", + "SMALL_GROUP_HIDDEN", + "SORTED_BY_REVENUE", + "TOTALS_AND_COUNTS", + "TWO_MAGAZINES" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1118/reference/subs_sum.prog.abap b/tasks_gen/train/G1118/reference/subs_sum.prog.abap new file mode 100644 index 0000000..c456052 --- /dev/null +++ b/tasks_gen/train/G1118/reference/subs_sum.prog.abap @@ -0,0 +1,111 @@ +REPORT {{p}}subs_sum. + +TYPES: BEGIN OF ty_line, + magazine TYPE {{p}}subs-magazine, + num_subs TYPE i, + revenue TYPE {{p}}subs-monthly_fee, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. +TYPES tt_magazine TYPE RANGE OF {{p}}subs-magazine. + +DATA gv_magazine TYPE {{p}}subs-magazine. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_mag FOR gv_magazine. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + CLASS-METHODS select_totals + IMPORTING iv_from TYPE d + it_magazine TYPE tt_magazine + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS keep_regulars + IMPORTING it_lines TYPE tt_line + RETURNING VALUE(rt_lines) TYPE tt_line. + CLASS-METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT magazine COUNT( * ) AS num_subs SUM( monthly_fee ) AS revenue + FROM {{p}}subs + INTO CORRESPONDING FIELDS OF TABLE rt_lines + WHERE sub_status = 'A' + AND start_date >= iv_from + AND magazine IN it_magazine + GROUP BY magazine. + ENDMETHOD. + + METHOD keep_regulars. + DATA ls_line TYPE ty_line. + LOOP AT it_lines INTO ls_line. + IF ls_line-num_subs >= 2. + APPEND ls_line TO rt_lines. + ENDIF. + ENDLOOP. + SORT rt_lines BY revenue DESCENDING. + ENDMETHOD. + + METHOD display. + DATA: lo_alv TYPE REF TO cl_salv_table, + lo_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lo_error. + MESSAGE lo_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA gt_lines TYPE tt_line. + gt_lines = lcl_report=>select_totals( iv_from = p_from it_magazine = s_mag[] ). + gt_lines = lcl_report=>keep_regulars( gt_lines ). + lcl_report=>display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS removes_small_groups FOR TESTING. + METHODS sorts_by_revenue FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD removes_small_groups. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'SMALL'. + ls_line-num_subs = 1. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'BIG'. + ls_line-num_subs = 2. + ls_line-revenue = 20. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_kept ) ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'BIG' act = ls_line-magazine ). + ENDMETHOD. + + METHOD sorts_by_revenue. + DATA: lt_lines TYPE tt_line, + lt_kept TYPE tt_line, + ls_line TYPE ty_line. + ls_line-magazine = 'LOW'. + ls_line-num_subs = 2. + ls_line-revenue = 10. + APPEND ls_line TO lt_lines. + ls_line-magazine = 'HIGH'. + ls_line-num_subs = 2. + ls_line-revenue = 30. + APPEND ls_line TO lt_lines. + lt_kept = lcl_report=>keep_regulars( lt_lines ). + READ TABLE lt_kept INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'HIGH' act = ls_line-magazine ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/seed/subs.tabl.asabap b/tasks_gen/train/G1118/seed/subs.tabl.asabap new file mode 100644 index 0000000..4703d89 --- /dev/null +++ b/tasks_gen/train/G1118/seed/subs.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Magazine subscriptions' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}subs { + key client : abap.clnt not null; + key subs_id : abap.int4 not null; + magazine : abap.char(12); + sub_status : abap.char(1); + start_date : abap.dats; + monthly_fee : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1118/seed/t15_seed.clas.abap b/tasks_gen/train/G1118/seed/t15_seed.clas.abap new file mode 100644 index 0000000..8862e8f --- /dev/null +++ b/tasks_gen/train/G1118/seed/t15_seed.clas.abap @@ -0,0 +1,20 @@ +CLASS {{p}}t15_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}t15_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}subs. + INSERT {{p}}subs FROM TABLE @( VALUE #( + ( subs_id = 1 magazine = 'ORBIT' sub_status = 'A' start_date = '20260101' monthly_fee = '12.00' ) + ( subs_id = 2 magazine = 'ORBIT' sub_status = 'A' start_date = '20260201' monthly_fee = '8.00' ) + ( subs_id = 3 magazine = 'ORBIT' sub_status = 'C' start_date = '20260101' monthly_fee = '20.00' ) + ( subs_id = 4 magazine = 'NATURE' sub_status = 'A' start_date = '20260115' monthly_fee = '30.00' ) + ( subs_id = 5 magazine = 'NATURE' sub_status = 'A' start_date = '20260301' monthly_fee = '10.00' ) + ( subs_id = 6 magazine = 'NATURE' sub_status = 'A' start_date = '20251201' monthly_fee = '5.00' ) + ( subs_id = 7 magazine = 'SPORT' sub_status = 'A' start_date = '20260110' monthly_fee = '25.00' ) + ( subs_id = 8 magazine = 'SPORT' sub_status = 'C' start_date = '20260210' monthly_fee = '15.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1118/spec.md b/tasks_gen/train/G1118/spec.md new file mode 100644 index 0000000..9303453 --- /dev/null +++ b/tasks_gen/train/G1118/spec.md @@ -0,0 +1,38 @@ +# 1. Goal +Show the subscriptions of each magazine in an ALV list. The publisher uses the +list to compare the magazines for a period. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}SUBS exists in package $TMP. It contains data. + Fields: SUBS_ID (INT4, key), MAGAZINE (CHAR 12), SUB_STATUS (CHAR 1), + START_DATE (DATS), MONTHLY_FEE (DEC 15,2). +- SUB_STATUS is 'A' for an active subscription and 'C' for a cancelled + subscription. + +# 4. Contract +- Create the executable program {{P}}SUBS_SUM in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first start date to include. + - Select-option S_MAG for the magazine. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: MAGAZINE, NUM_SUBS (number of included subscriptions), + REVENUE (sum of MONTHLY_FEE). + +# 5. Business rules +1. Include a subscription only if SUB_STATUS is 'A', START_DATE is on or after + P_FROM, and MAGAZINE is in S_MAG. +2. Show one line for each magazine that has at least two included subscriptions. +3. Sort the lines by REVENUE, the highest revenue first. + +# 6. Constraints +- Release target: 7.40 SP05. Use only syntax that this release supports. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}SUBS. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1118/task.json b/tasks_gen/train/G1118/task.json new file mode 100644 index 0000000..10ceb36 --- /dev/null +++ b/tasks_gen/train/G1118/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1118", + "category": "G", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v740sp05", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SUBS", + "file": "seed/subs.tabl.asabap", + "description": "Magazine subscriptions" + }, + { + "type": "CLAS", + "name": "{{P}}T15_SEED", + "file": "seed/t15_seed.clas.abap", + "description": "T15 seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}SUBS_SUM", + "parameters": [ + "p_from", + "s_mag" + ] + } + ], + "out_of_scope": [ + "{{P}}SUBS" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T15_HIDDEN", + "file": "hidden/t15_hidden.clas.abap", + "description": "T15 hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}SUBS_SUM", + "file": "reference/subs_sum.prog.abap", + "description": "Subscriptions per magazine" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1119/generation.json b/tasks_gen/train/G1119/generation.json new file mode 100644 index 0000000..5e66380 --- /dev/null +++ b/tasks_gen/train/G1119/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1119", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1119/reference/bind_fee.func.abap b/tasks_gen/train/G1119/reference/bind_fee.func.abap new file mode 100644 index 0000000..7f0e4ad --- /dev/null +++ b/tasks_gen/train/G1119/reference/bind_fee.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}bind_fee + IMPORTING + iv_binding TYPE string + iv_pages TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_rush TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_pages. + + CONSTANTS: lc_rate_pb TYPE decfloat34 VALUE '0.05', + lc_rate_hb TYPE decfloat34 VALUE '0.12', + lc_rate_ex TYPE decfloat34 VALUE '0.20', + lc_min_hb TYPE decfloat34 VALUE '15.00', + lc_discount TYPE decfloat34 VALUE '0.9'. + + ev_rush = abap_false. + IF iv_pages < 1. + RAISE invalid_pages. + ENDIF. + + CASE iv_binding. + WHEN 'PB'. + cv_fee = round( val = iv_pages * lc_rate_pb + dec = 2 + mode = cl_abap_math=>round_half_up ). + WHEN 'HB'. + cv_fee = round( val = iv_pages * lc_rate_hb + dec = 2 + mode = cl_abap_math=>round_half_up ). + IF cv_fee < lc_min_hb. + cv_fee = lc_min_hb. + ENDIF. + WHEN 'EX'. + ev_rush = abap_true. + cv_fee = round( val = iv_pages * lc_rate_ex + dec = 2 + mode = cl_abap_math=>round_half_up ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF iv_member = abap_true AND cv_fee > 0. + cv_fee = round( val = cv_fee * lc_discount + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1119/reference/bind_test.clas.abap b/tasks_gen/train/G1119/reference/bind_test.clas.abap new file mode 100644 index 0000000..0107b49 --- /dev/null +++ b/tasks_gen/train/G1119/reference/bind_test.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}bind_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_binding TYPE string + iv_pages TYPE i + iv_member TYPE abap_bool DEFAULT abap_false + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS paperback_fee FOR TESTING. + METHODS hardback_minimum FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS express_sets_rush FOR TESTING. + METHODS other_type_zero FOR TESTING. + METHODS invalid_pages FOR TESTING. +ENDCLASS. + + +CLASS {{p}}bind_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_rush TYPE abap_bool. + CALL FUNCTION '{{P}}BIND_FEE' + EXPORTING + iv_binding = iv_binding + iv_pages = iv_pages + iv_member = iv_member + IMPORTING + ev_rush = lv_rush + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_pages = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD paperback_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '10.00' ) + act = fee_for( iv_binding = `PB` iv_pages = 200 ) ). + ENDMETHOD. + + METHOD hardback_minimum. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '15.00' ) + act = fee_for( iv_binding = `HB` iv_pages = 50 ) ). + ENDMETHOD. + + METHOD member_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '9.00' ) + act = fee_for( iv_binding = `PB` iv_pages = 200 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD express_sets_rush. + DATA lv_rush TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}BIND_FEE' + EXPORTING + iv_binding = `EX` + iv_pages = 10 + iv_member = abap_false + IMPORTING + ev_rush = lv_rush + CHANGING + cv_fee = lv_fee. + cl_abap_unit_assert=>assert_true( lv_rush ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD other_type_zero. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 0 ) + act = fee_for( iv_binding = `XX` iv_pages = 100 ) ). + ENDMETHOD. + + METHOD invalid_pages. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}BIND_FEE' + EXPORTING + iv_binding = `PB` + iv_pages = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_pages = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1119/seed/bind_sample.func.abap b/tasks_gen/train/G1119/seed/bind_sample.func.abap new file mode 100644 index 0000000..5b2089d --- /dev/null +++ b/tasks_gen/train/G1119/seed/bind_sample.func.abap @@ -0,0 +1,19 @@ +FUNCTION {{p}}bind_sample + IMPORTING + iv_binding TYPE string + iv_pages TYPE i + iv_member TYPE abap_bool + EXPORTING + ev_rush TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_pages. + + ev_rush = abap_false. + IF iv_pages < 1. + RAISE invalid_pages. + ENDIF. + cv_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1119/spec.md b/tasks_gen/train/G1119/spec.md new file mode 100644 index 0000000..a5c950a --- /dev/null +++ b/tasks_gen/train/G1119/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +A bookbinding workshop prices binding jobs. The order desk calls one function +module for each job. The function module calculates the fee of the job and +reports whether the job is a rush job. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_BIND exists in package $TMP. +- Function module {{P}}BIND_SAMPLE exists in this group. It shows the interface + that the order desk uses. + +# 4. Contract +- Create the function module {{P}}BIND_FEE in function group {{P}}FG_BIND. +- The interface of {{P}}BIND_FEE: + - importing iv_binding TYPE string + - importing iv_pages TYPE i + - importing iv_member TYPE abap_bool + - exporting ev_rush TYPE abap_bool + - changing cv_fee TYPE decfloat34 + - exceptions invalid_pages + +# 5. Business rules +1. If the number of pages is less than 1, raise the exception INVALID_PAGES. +2. Set the rush flag to false, except in rule 7. +3. Binding type PB: the fee is 0.05 per page, rounded half up to 2 decimals. +4. Binding type HB: the fee is 0.12 per page, rounded half up to 2 decimals. + The minimum fee for hardback binding is 15.00. +5. A member receives a discount of 10 % on the fee. The discount is the last + step of the calculation and is applied after the minimum fee of rule 4. +6. The minimum fee of rule 4 is the last step of the calculation and is applied + after all discounts. +7. Binding type EX: set the rush flag to true. The fee is 0.20 per page, + rounded half up to 2 decimals. +8. Other binding types: the fee is 0. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}BIND_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1119/task.json b/tasks_gen/train/G1119/task.json new file mode 100644 index 0000000..1fc100a --- /dev/null +++ b/tasks_gen/train/G1119/task.json @@ -0,0 +1,100 @@ +{ + "id": "G1119", + "category": "H", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "Rules 5 and 6 both claim to be the last step of the calculation, so the order of the member discount and the hardback minimum fee contradicts itself and the fee of a hardback job for a member cannot be calculated.", + "keywords": [ + [ + "minimum", + "discount", + "last" + ], + [ + "minimum", + "discount", + "priority" + ], + [ + "minimum", + "discount", + "order" + ], + [ + "contradict" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_BIND", + "description": "Function group for binding fees" + }, + { + "type": "FUNC", + "name": "{{P}}BIND_SAMPLE", + "functionGroup": "{{P}}FG_BIND", + "file": "seed/bind_sample.func.abap", + "description": "Sample binding module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}BIND_FEE", + "functionGroup": "{{P}}FG_BIND", + "params": [ + { + "name": "iv_binding", + "type": "string" + }, + { + "name": "iv_pages", + "type": "i" + }, + { + "name": "iv_member", + "type": "abap_bool" + }, + { + "name": "ev_rush", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}BIND_SAMPLE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}BIND_FEE", + "functionGroup": "{{P}}FG_BIND", + "file": "reference/bind_fee.func.abap", + "description": "Binding fee module" + }, + { + "type": "CLAS", + "name": "{{P}}BIND_TEST", + "file": "reference/bind_test.clas.abap", + "description": "Own tests for the binding fee module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1120/faulty/m0_i_brick_size.ddls.asddls b/tasks_gen/train/G1120/faulty/m0_i_brick_size.ddls.asddls new file mode 100644 index 0000000..b344109 --- /dev/null +++ b/tasks_gen/train/G1120/faulty/m0_i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 2, 2 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g >= 5000 then 'L' + when weight_g >= 2000 then 'M' + else 'S' + end as size_class +} diff --git a/tasks_gen/train/G1120/faulty/m1_i_brick_size.ddls.asddls b/tasks_gen/train/G1120/faulty/m1_i_brick_size.ddls.asddls new file mode 100644 index 0000000..4a88b5e --- /dev/null +++ b/tasks_gen/train/G1120/faulty/m1_i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 1, 3 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g >= 5000 then 'L' + when weight_g >= 2000 then 'M' + else 'S' + end as size_class +} diff --git a/tasks_gen/train/G1120/faulty/m2_i_brick_size.ddls.asddls b/tasks_gen/train/G1120/faulty/m2_i_brick_size.ddls.asddls new file mode 100644 index 0000000..b02b296 --- /dev/null +++ b/tasks_gen/train/G1120/faulty/m2_i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 1, 2 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g < 5000 then 'L' + when weight_g >= 2000 then 'M' + else 'S' + end as size_class +} diff --git a/tasks_gen/train/G1120/faulty/m3_i_brick_size.ddls.asddls b/tasks_gen/train/G1120/faulty/m3_i_brick_size.ddls.asddls new file mode 100644 index 0000000..8e67fd1 --- /dev/null +++ b/tasks_gen/train/G1120/faulty/m3_i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 1, 2 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g >= 5000 then 'Z' + when weight_g >= 2000 then 'M' + else 'S' + end as size_class +} diff --git a/tasks_gen/train/G1120/faulty/m4_i_brick_size.ddls.asddls b/tasks_gen/train/G1120/faulty/m4_i_brick_size.ddls.asddls new file mode 100644 index 0000000..d32014a --- /dev/null +++ b/tasks_gen/train/G1120/faulty/m4_i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 1, 2 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g >= 5000 then 'L' + when weight_g >= 2000 then 'M' + else 'Z' + end as size_class +} diff --git a/tasks_gen/train/G1120/generation.json b/tasks_gen/train/G1120/generation.json new file mode 100644 index 0000000..992f3fa --- /dev/null +++ b/tasks_gen/train/G1120/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1120", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1120/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1120/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..68d17f4 --- /dev/null +++ b/tasks_gen/train/G1120/hidden/t31_hidden.clas.abap @@ -0,0 +1,86 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS all_bricks_shown FOR TESTING. + METHODS mould_kept FOR TESTING. + METHODS mould_family FOR TESTING. + METHODS volume_of_brick FOR TESTING. + METHODS weight_in_kg FOR TESTING. + METHODS class_large FOR TESTING. + METHODS class_medium FOR TESTING. + METHODS class_small FOR TESTING. + METHODS class_boundary FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BRICK_SIZE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_brick TYPE STANDARD TABLE OF {{p}}brick WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_brick = VALUE #( + ( brick_id = 'B1' mould = 'NF01' length_mm = 240 width_mm = 115 height_mm = 71 weight_g = 3200 ) + ( brick_id = 'B2' mould = 'WF02' length_mm = 300 width_mm = 200 height_mm = 150 weight_g = 8000 ) + ( brick_id = 'B3' mould = 'NF03' length_mm = 200 width_mm = 100 height_mm = 50 weight_g = 1500 ) + ( brick_id = 'B4' mould = 'BLK1' length_mm = 250 width_mm = 120 height_mm = 80 weight_g = 5000 ) ). + go_env->insert_test_data( lt_brick ). + ENDMETHOD. + + METHOD all_bricks_shown. + SELECT COUNT(*) FROM {{p}}i_brick_size INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rows ). + ENDMETHOD. + + METHOD mould_kept. + SELECT SINGLE mould FROM {{p}}i_brick_size WHERE brick_id = 'B2' INTO @DATA(lv_mould). + cl_abap_unit_assert=>assert_equals( exp = 'WF02' act = lv_mould ). + ENDMETHOD. + + METHOD mould_family. + SELECT SINGLE mould_family FROM {{p}}i_brick_size WHERE brick_id = 'B4' INTO @DATA(lv_family). + cl_abap_unit_assert=>assert_equals( exp = 'BL' act = lv_family ). + ENDMETHOD. + + METHOD volume_of_brick. + SELECT SINGLE volume_cm3 FROM {{p}}i_brick_size WHERE brick_id = 'B1' INTO @DATA(lv_volume). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1959.6' ) + act = CONV decfloat34( lv_volume ) ). + ENDMETHOD. + + METHOD weight_in_kg. + SELECT SINGLE weight_kg FROM {{p}}i_brick_size WHERE brick_id = 'B2' INTO @DATA(lv_weight). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8' ) + act = CONV decfloat34( lv_weight ) ). + ENDMETHOD. + + METHOD class_large. + SELECT SINGLE size_class FROM {{p}}i_brick_size WHERE brick_id = 'B2' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_class ). + ENDMETHOD. + + METHOD class_medium. + SELECT SINGLE size_class FROM {{p}}i_brick_size WHERE brick_id = 'B1' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_class ). + ENDMETHOD. + + METHOD class_small. + SELECT SINGLE size_class FROM {{p}}i_brick_size WHERE brick_id = 'B3' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lv_class ). + ENDMETHOD. + + METHOD class_boundary. + SELECT SINGLE size_class FROM {{p}}i_brick_size WHERE brick_id = 'B4' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_class ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1120/mutation.json b/tasks_gen/train/G1120/mutation.json new file mode 100644 index 0000000..47a7a5a --- /dev/null +++ b/tasks_gen/train/G1120/mutation.json @@ -0,0 +1,58 @@ +{ + "task": "G1120", + "mutants": [ + { + "object": "{{P}}I_BRICK_SIZE", + "mutant": "line 8: 1 -> 2 (const)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "MOULD_FAMILY" + ] + }, + { + "object": "{{P}}I_BRICK_SIZE", + "mutant": "line 8: 2 -> 3 (const)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "MOULD_FAMILY" + ] + }, + { + "object": "{{P}}I_BRICK_SIZE", + "mutant": "line 14: >= -> < (rel)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "CLASS_BOUNDARY", + "CLASS_LARGE", + "CLASS_MEDIUM", + "CLASS_SMALL" + ] + }, + { + "object": "{{P}}I_BRICK_SIZE", + "mutant": "line 14: 'L' -> 'Z' (lit)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "CLASS_BOUNDARY", + "CLASS_LARGE" + ] + }, + { + "object": "{{P}}I_BRICK_SIZE", + "mutant": "line 16: 'S' -> 'Z' (lit)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "CLASS_SMALL" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1120/reference/i_brick_size.ddls.asddls b/tasks_gen/train/G1120/reference/i_brick_size.ddls.asddls new file mode 100644 index 0000000..08a5088 --- /dev/null +++ b/tasks_gen/train/G1120/reference/i_brick_size.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Brick size data' +define view entity {{P}}I_BRICK_SIZE + as select from {{p}}brick +{ + key brick_id as brick_id, + mould as mould, + substring( mould, 1, 2 ) as mould_family, + cast( cast( length_mm * width_mm * height_mm as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as volume_cm3, + cast( cast( weight_g as abap.dec( 15, 3 ) ) + / 1000 as abap.dec( 15, 3 ) ) as weight_kg, + case + when weight_g >= 5000 then 'L' + when weight_g >= 2000 then 'M' + else 'S' + end as size_class +} diff --git a/tasks_gen/train/G1120/reference/t31_test.clas.abap b/tasks_gen/train/G1120/reference/t31_test.clas.abap new file mode 100644 index 0000000..ad93412 --- /dev/null +++ b/tasks_gen/train/G1120/reference/t31_test.clas.abap @@ -0,0 +1,39 @@ +CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS size_data_of_one_brick FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BRICK_SIZE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_brick TYPE STANDARD TABLE OF {{p}}brick WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_brick = VALUE #( ( brick_id = 'X1' mould = 'NF01' length_mm = 240 width_mm = 115 + height_mm = 71 weight_g = 3200 ) ). + go_env->insert_test_data( lt_brick ). + ENDMETHOD. + + METHOD size_data_of_one_brick. + SELECT SINGLE mould_family, volume_cm3, weight_kg, size_class FROM {{p}}i_brick_size + WHERE brick_id = 'X1' INTO @DATA(ls_brick). + cl_abap_unit_assert=>assert_equals( exp = 'NF' act = ls_brick-mould_family ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1959.6' ) + act = CONV decfloat34( ls_brick-volume_cm3 ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.2' ) + act = CONV decfloat34( ls_brick-weight_kg ) ). + cl_abap_unit_assert=>assert_equals( exp = 'M' act = ls_brick-size_class ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1120/seed/brick.tabl.asabap b/tasks_gen/train/G1120/seed/brick.tabl.asabap new file mode 100644 index 0000000..3957332 --- /dev/null +++ b/tasks_gen/train/G1120/seed/brick.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Brick master data' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}brick { + key client : abap.clnt not null; + key brick_id : abap.char(10) not null; + mould : abap.char(4); + length_mm : abap.int4; + width_mm : abap.int4; + height_mm : abap.int4; + weight_g : abap.int4; +} diff --git a/tasks_gen/train/G1120/spec.md b/tasks_gen/train/G1120/spec.md new file mode 100644 index 0000000..3ee6160 --- /dev/null +++ b/tasks_gen/train/G1120/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A brickworks produces bricks in different moulds. The kiln team needs one row for +each brick with the size data of the brick. A report and an OData service will +read this data. Therefore the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}BRICK (brick master data), package $TMP. Key: BRICK_ID (CHAR 10). + Fields: + - MOULD (CHAR 4): the mould code. A mould code always has 4 characters. + - LENGTH_MM (INT4): the length of the brick in millimetres. + - WIDTH_MM (INT4): the width of the brick in millimetres. + - HEIGHT_MM (INT4): the height of the brick in millimetres. + - WEIGHT_G (INT4): the weight of the brick in grams. + +# 4. Contract +- Create the CDS view entity {{P}}I_BRICK_SIZE in package $TMP. +- The view has these elements, with these names: + - BRICK_ID (key) + - MOULD + - MOULD_FAMILY + - VOLUME_CM3 + - WEIGHT_KG + - SIZE_CLASS +- Use the authorization check #NOT_REQUIRED. + +# 5. Business rules +1. The view shows one row for each row of the table {{P}}BRICK. +2. MOULD is the mould code of the brick. +3. MOULD_FAMILY contains the first two characters of the mould code. +4. VOLUME_CM3 is the volume of the brick in cubic centimetres. Calculate the + volume as LENGTH_MM multiplied by WIDTH_MM multiplied by HEIGHT_MM, divided by + 1000. The value has 3 decimals. +5. WEIGHT_KG is the weight of the brick in kilograms. Calculate the weight as + WEIGHT_G divided by 1000. The value has 3 decimals. +6. SIZE_CLASS is the weight class of the brick: + 1. The class is 'L' if WEIGHT_G is 5000 or more. + 2. The class is 'M' if WEIGHT_G is 2000 or more and less than 5000. + 3. The class is 'S' if WEIGHT_G is less than 2000. + +# 6. Constraints +- Release target: 8.16. +- Out of scope: do not change the table {{P}}BRICK. +- Use Clean ABAP. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1120/task.json b/tasks_gen/train/G1120/task.json new file mode 100644 index 0000000..a9823ad --- /dev/null +++ b/tasks_gen/train/G1120/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1120", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}BRICK", + "file": "seed/brick.tabl.asabap", + "description": "Brick master data" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_BRICK_SIZE", + "fields": [ + "BRICK_ID", + "MOULD", + "MOULD_FAMILY", + "VOLUME_CM3", + "WEIGHT_KG", + "SIZE_CLASS" + ] + } + ], + "out_of_scope": [ + "{{P}}BRICK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_BRICK_SIZE", + "file": "reference/i_brick_size.ddls.asddls", + "description": "Brick size data" + }, + { + "type": "CLAS", + "name": "{{P}}T31_TEST", + "file": "reference/t31_test.clas.abap", + "description": "T31 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1121/faulty/m0_mush_harvest.func.abap b/tasks_gen/train/G1121/faulty/m0_mush_harvest.func.abap new file mode 100644 index 0000000..93dcbf6 --- /dev/null +++ b/tasks_gen/train/G1121/faulty/m0_mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + OR grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a OR room_id = @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id <> lv_room. + ev_room_count = ev_room_count + 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/faulty/m1_mush_harvest.func.abap b/tasks_gen/train/G1121/faulty/m1_mush_harvest.func.abap new file mode 100644 index 0000000..b092254 --- /dev/null +++ b/tasks_gen/train/G1121/faulty/m1_mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + AND grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a AND room_id = @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id <> lv_room. + ev_room_count = ev_room_count + 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/faulty/m2_mush_harvest.func.abap b/tasks_gen/train/G1121/faulty/m2_mush_harvest.func.abap new file mode 100644 index 0000000..39eae8b --- /dev/null +++ b/tasks_gen/train/G1121/faulty/m2_mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + AND grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a OR room_id <> @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id <> lv_room. + ev_room_count = ev_room_count + 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/faulty/m3_mush_harvest.func.abap b/tasks_gen/train/G1121/faulty/m3_mush_harvest.func.abap new file mode 100644 index 0000000..5cb0675 --- /dev/null +++ b/tasks_gen/train/G1121/faulty/m3_mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + AND grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a OR room_id = @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id = lv_room. + ev_room_count = ev_room_count + 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/faulty/m4_mush_harvest.func.abap b/tasks_gen/train/G1121/faulty/m4_mush_harvest.func.abap new file mode 100644 index 0000000..1a90e78 --- /dev/null +++ b/tasks_gen/train/G1121/faulty/m4_mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + AND grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a OR room_id = @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id <> lv_room. + ev_room_count = ev_room_count - 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/generation.json b/tasks_gen/train/G1121/generation.json new file mode 100644 index 0000000..a788d17 --- /dev/null +++ b/tasks_gen/train/G1121/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1121", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/harvest.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/harvest.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1121/hidden/t_mush_hidden.clas.abap b/tasks_gen/train/G1121/hidden/t_mush_hidden.clas.abap new file mode 100644 index 0000000..d973551 --- /dev/null +++ b/tasks_gen/train/G1121/hidden/t_mush_hidden.clas.abap @@ -0,0 +1,171 @@ +CLASS {{p}}t_mush_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS setup. + METHODS teardown. + METHODS add_batch + IMPORTING iv_room TYPE string iv_grade TYPE string + iv_batch TYPE string iv_weight TYPE decfloat34. + METHODS call + IMPORTING iv_a TYPE string iv_b TYPE string + EXPORTING ev_total TYPE decfloat34 ev_rooms TYPE i + ev_grade TYPE char1 ev_subrc TYPE sysubrc. + METHODS totals_two_rooms FOR TESTING. + METHODS ignores_other_room FOR TESTING. + METHODS ignores_bad_grade FOR TESTING. + METHODS ignores_zero_weight FOR TESTING. + METHODS ignores_negative_weight FOR TESTING. + METHODS counts_rooms FOR TESTING. + METHODS top_grade_by_weight FOR TESTING. + METHODS top_grade_tie FOR TESTING. + METHODS empty_table_raises FOR TESTING. + METHODS no_counting_row_raises FOR TESTING. + METHODS ignores_empty_room FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_mush_hidden IMPLEMENTATION. + METHOD setup. + DELETE FROM {{P}}HARVEST. + ENDMETHOD. + + METHOD teardown. + DELETE FROM {{P}}HARVEST. + ENDMETHOD. + + METHOD add_batch. + DATA ls_row TYPE {{P}}HARVEST. + ls_row-room_id = iv_room. + ls_row-grade = iv_grade. + ls_row-batch_no = iv_batch. + ls_row-weight_kg = iv_weight. + INSERT {{P}}HARVEST FROM @ls_row. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD call. + CALL FUNCTION '{{P}}MUSH_HARVEST' + EXPORTING + iv_room_a = iv_a + iv_room_b = iv_b + IMPORTING + ev_total_kg = ev_total + ev_room_count = ev_rooms + ev_top_grade = ev_grade + EXCEPTIONS + no_stock = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD totals_two_rooms. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '10.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '2.500' ). + add_batch( iv_room = 'R02' iv_grade = 'A' iv_batch = '001' iv_weight = '1.500' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '14' ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rooms ). + ENDMETHOD. + + METHOD ignores_other_room. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '4.000' ). + add_batch( iv_room = 'R03' iv_grade = 'A' iv_batch = '001' iv_weight = '9.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rooms ). + ENDMETHOD. + + METHOD ignores_bad_grade. + add_batch( iv_room = 'R01' iv_grade = 'D' iv_batch = '001' iv_weight = '7.000' ). + add_batch( iv_room = 'R01' iv_grade = 'a' iv_batch = '002' iv_weight = '7.000' ). + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '003' iv_weight = '1.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_total ). + ENDMETHOD. + + METHOD ignores_zero_weight. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '0.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '4.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_grade = DATA(lv_grade) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lv_grade ). + ENDMETHOD. + + METHOD ignores_negative_weight. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '-3.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '4.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = lv_total ). + ENDMETHOD. + + METHOD counts_rooms. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '5.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '5.000' ). + add_batch( iv_room = 'R02' iv_grade = 'C' iv_batch = '001' iv_weight = '5.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rooms ). + ENDMETHOD. + + METHOD top_grade_by_weight. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '1.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '9.000' ). + add_batch( iv_room = 'R02' iv_grade = 'C' iv_batch = '001' iv_weight = '2.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_grade = DATA(lv_grade) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lv_grade ). + ENDMETHOD. + + METHOD top_grade_tie. + add_batch( iv_room = 'R01' iv_grade = 'C' iv_batch = '001' iv_weight = '5.000' ). + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '002' iv_weight = '5.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R01' + IMPORTING ev_grade = DATA(lv_grade) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lv_grade ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rooms ). + ENDMETHOD. + + METHOD empty_table_raises. + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) + ev_grade = DATA(lv_grade) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rooms ). + cl_abap_unit_assert=>assert_initial( act = lv_grade ). + ENDMETHOD. + + METHOD no_counting_row_raises. + add_batch( iv_room = 'R01' iv_grade = 'D' iv_batch = '001' iv_weight = '5.000' ). + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '002' iv_weight = '0.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rooms ). + ENDMETHOD. + + METHOD ignores_empty_room. + add_batch( iv_room = '' iv_grade = 'A' iv_batch = '001' iv_weight = '9.000' ). + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '1.000' ). + call( EXPORTING iv_a = '' iv_b = 'R01' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rooms ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1121/mutation.json b/tasks_gen/train/G1121/mutation.json new file mode 100644 index 0000000..e2c00a5 --- /dev/null +++ b/tasks_gen/train/G1121/mutation.json @@ -0,0 +1,76 @@ +{ + "task": "G1121", + "mutants": [ + { + "object": "{{P}}MUSH_HARVEST", + "mutant": "line 21: AND -> OR (logic)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "IGNORES_BAD_GRADE", + "IGNORES_EMPTY_ROOM", + "IGNORES_NEGATIVE_WEIGHT", + "IGNORES_OTHER_ROOM", + "NO_COUNTING_ROW_RAISES" + ] + }, + { + "object": "{{P}}MUSH_HARVEST", + "mutant": "line 23: OR -> AND (logic)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "COUNTS_ROOMS", + "IGNORES_BAD_GRADE", + "IGNORES_EMPTY_ROOM", + "IGNORES_NEGATIVE_WEIGHT", + "IGNORES_OTHER_ROOM", + "IGNORES_ZERO_WEIGHT", + "TOP_GRADE_BY_WEIGHT", + "TOTALS_TWO_ROOMS" + ] + }, + { + "object": "{{P}}MUSH_HARVEST", + "mutant": "line 23: = -> <> (eq)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "COUNTS_ROOMS", + "IGNORES_EMPTY_ROOM", + "IGNORES_OTHER_ROOM", + "TOTALS_TWO_ROOMS" + ] + }, + { + "object": "{{P}}MUSH_HARVEST", + "mutant": "line 49: <> -> = (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "COUNTS_ROOMS", + "IGNORES_EMPTY_ROOM", + "IGNORES_OTHER_ROOM", + "TOP_GRADE_TIE", + "TOTALS_TWO_ROOMS" + ] + }, + { + "object": "{{P}}MUSH_HARVEST", + "mutant": "line 50: + -> - (arith)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "COUNTS_ROOMS", + "IGNORES_EMPTY_ROOM", + "IGNORES_OTHER_ROOM", + "TOP_GRADE_TIE", + "TOTALS_TWO_ROOMS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1121/reference/mush_harvest.func.abap b/tasks_gen/train/G1121/reference/mush_harvest.func.abap new file mode 100644 index 0000000..bcf0245 --- /dev/null +++ b/tasks_gen/train/G1121/reference/mush_harvest.func.abap @@ -0,0 +1,62 @@ +FUNCTION {{p}}mush_harvest + IMPORTING + iv_room_a TYPE string + iv_room_b TYPE string + EXPORTING + ev_total_kg TYPE decfloat34 + ev_room_count TYPE i + ev_top_grade TYPE char1 + EXCEPTIONS + no_stock. + + DATA lt_rows TYPE STANDARD TABLE OF {{P}}HARVEST. + DATA lt_stock TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY room_id grade. + DATA lt_grade TYPE SORTED TABLE OF {{P}}HARVEST WITH UNIQUE KEY grade. + DATA lv_room TYPE {{P}}HARVEST-room_id. + DATA lv_best TYPE {{P}}HARVEST-weight_kg. + + SELECT room_id, grade, batch_no, weight_kg + FROM {{P}}HARVEST + WHERE room_id <> '' + AND grade IN ( 'A', 'B', 'C' ) + AND weight_kg > 0 + AND ( room_id = @iv_room_a OR room_id = @iv_room_b ) + INTO TABLE @lt_rows. + + IF lt_rows IS INITIAL. + RAISE no_stock. + ENDIF. + + LOOP AT lt_rows ASSIGNING FIELD-SYMBOL(). + ev_total_kg = ev_total_kg + -weight_kg. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY room_id = -room_id grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_stock. + ENDIF. + READ TABLE lt_grade ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY grade = -grade. + IF sy-subrc = 0. + -weight_kg = -weight_kg + -weight_kg. + ELSE. + INSERT INTO TABLE lt_grade. + ENDIF. + ENDLOOP. + + LOOP AT lt_stock INTO DATA(ls_stock). + IF ls_stock-room_id <> lv_room. + ev_room_count = ev_room_count + 1. + lv_room = ls_stock-room_id. + ENDIF. + ENDLOOP. + + LOOP AT lt_grade INTO DATA(ls_grade). + IF ls_grade-weight_kg > lv_best. + lv_best = ls_grade-weight_kg. + ev_top_grade = ls_grade-grade. + ENDIF. + ENDLOOP. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1121/reference/t_mush_test.clas.abap b/tasks_gen/train/G1121/reference/t_mush_test.clas.abap new file mode 100644 index 0000000..e0a9bfa --- /dev/null +++ b/tasks_gen/train/G1121/reference/t_mush_test.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}t_mush_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS setup. + METHODS teardown. + METHODS add_batch + IMPORTING iv_room TYPE string iv_grade TYPE string + iv_batch TYPE string iv_weight TYPE decfloat34. + METHODS call + IMPORTING iv_a TYPE string iv_b TYPE string + EXPORTING ev_total TYPE decfloat34 ev_rooms TYPE i + ev_grade TYPE char1 ev_subrc TYPE sysubrc. + METHODS sums_two_rooms FOR TESTING. + METHODS ignores_bad_records FOR TESTING. + METHODS finds_top_grade FOR TESTING. + METHODS raises_without_records FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_mush_test IMPLEMENTATION. + METHOD setup. + DELETE FROM {{P}}HARVEST. + ENDMETHOD. + + METHOD teardown. + DELETE FROM {{P}}HARVEST. + ENDMETHOD. + + METHOD add_batch. + DATA ls_row TYPE {{P}}HARVEST. + ls_row-room_id = iv_room. + ls_row-grade = iv_grade. + ls_row-batch_no = iv_batch. + ls_row-weight_kg = iv_weight. + INSERT {{P}}HARVEST FROM @ls_row. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD call. + CALL FUNCTION '{{P}}MUSH_HARVEST' + EXPORTING + iv_room_a = iv_a + iv_room_b = iv_b + IMPORTING + ev_total_kg = ev_total + ev_room_count = ev_rooms + ev_top_grade = ev_grade + EXCEPTIONS + no_stock = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD sums_two_rooms. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '10.000' ). + add_batch( iv_room = 'R02' iv_grade = 'B' iv_batch = '001' iv_weight = '5.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_rooms = DATA(lv_rooms) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = lv_total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rooms ). + ENDMETHOD. + + METHOD ignores_bad_records. + add_batch( iv_room = 'R01' iv_grade = 'D' iv_batch = '001' iv_weight = '4.000' ). + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '002' iv_weight = '0.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '003' iv_weight = '3.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = lv_total ). + ENDMETHOD. + + METHOD finds_top_grade. + add_batch( iv_room = 'R01' iv_grade = 'A' iv_batch = '001' iv_weight = '1.000' ). + add_batch( iv_room = 'R01' iv_grade = 'B' iv_batch = '002' iv_weight = '9.000' ). + call( EXPORTING iv_a = 'R01' iv_b = 'R01' + IMPORTING ev_grade = DATA(lv_grade) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lv_grade ). + ENDMETHOD. + + METHOD raises_without_records. + call( EXPORTING iv_a = 'R01' iv_b = 'R02' + IMPORTING ev_total = DATA(lv_total) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_total ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1121/seed/harvest.tabl.abap b/tasks_gen/train/G1121/seed/harvest.tabl.abap new file mode 100644 index 0000000..30530eb --- /dev/null +++ b/tasks_gen/train/G1121/seed/harvest.tabl.abap @@ -0,0 +1,10 @@ +@EndUserText.label: 'Harvest record of a growing room' +@AbapCatalog.tableCategory: #TRANSPARENT +@AbapCatalog.deliveryClass: #A +@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE +define table {{P}}HARVEST { + key room_id : abap.char(4); + key grade : abap.char(1); + key batch_no : abap.char(3); + weight_kg : abap.dec(13,3); +} diff --git a/tasks_gen/train/G1121/spec.md b/tasks_gen/train/G1121/spec.md new file mode 100644 index 0000000..ee58bdd --- /dev/null +++ b/tasks_gen/train/G1121/spec.md @@ -0,0 +1,54 @@ +# 1. Goal +Summarise the harvest of two growing rooms of a mushroom farm. The store keeper +calls one function module for the two rooms. The function module reads the +harvest records of the two rooms and returns the summary. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_MUSH exists in package $TMP. +- Table {{P}}HARVEST exists in package $TMP. It holds the harvest records of + all growing rooms. Its fields are room_id, grade, batch_no and weight_kg. One + record is the weight of one batch of one grade in one room. +- The function module reads the harvest records of the two rooms from this + table. + +# 4. Contract +- Create the function module {{P}}MUSH_HARVEST in function group {{P}}FG_MUSH. +- Interface of {{P}}MUSH_HARVEST: + IMPORTING iv_room_a TYPE string + IMPORTING iv_room_b TYPE string + EXPORTING ev_total_kg TYPE decfloat34 + EXPORTING ev_room_count TYPE i + EXPORTING ev_top_grade TYPE char1 + EXCEPTIONS no_stock + +# 5. Business rules +1. A harvest record counts only if all of these conditions are true: + - its room_id is not empty, + - its room_id is equal to iv_room_a or to iv_room_b, + - its grade is 'A', 'B' or 'C', + - its weight_kg is greater than zero. +2. A record that does not count is ignored. It does not cause an error. +3. ev_total_kg is the sum of the weight_kg of all counting records. If no + record counts, ev_total_kg is 0. +4. ev_room_count is the number of different room_id values among the counting + records. If no record counts, ev_room_count is 0. +5. ev_top_grade is the grade with the highest sum of weight_kg among the + counting records. If two grades have the same sum, ev_top_grade is the grade + that comes first in alphabetical order. If no record counts, ev_top_grade is + initial. +6. If no record counts, the function module raises the exception NO_STOCK. +7. If at least one record counts, the function module does not raise an + exception. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Keep the function module below 40 statements. +- Out of scope: do not change table {{P}}HARVEST. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1121/task.json b/tasks_gen/train/G1121/task.json new file mode 100644 index 0000000..8806c08 --- /dev/null +++ b/tasks_gen/train/G1121/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1121", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_MUSH", + "description": "Functions of the mushroom farm" + }, + { + "type": "TABL", + "name": "{{P}}HARVEST", + "file": "seed/harvest.tabl.abap", + "description": "Harvest record of a growing room" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}MUSH_HARVEST", + "functionGroup": "{{P}}FG_MUSH", + "params": [ + { + "name": "iv_room_a", + "type": "string" + }, + { + "name": "iv_room_b", + "type": "string" + }, + { + "name": "ev_total_kg", + "type": "decfloat34" + }, + { + "name": "ev_room_count", + "type": "i" + }, + { + "name": "ev_top_grade", + "type": "char1" + } + ] + } + ], + "out_of_scope": [ + "{{P}}HARVEST" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_MUSH_HIDDEN", + "file": "hidden/t_mush_hidden.clas.abap", + "description": "Hidden tests for {{P}}MUSH_HARVEST" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}MUSH_HARVEST", + "functionGroup": "{{P}}FG_MUSH", + "file": "reference/mush_harvest.func.abap", + "description": "Summarises the harvest of two growing rooms" + }, + { + "type": "CLAS", + "name": "{{P}}T_MUSH_TEST", + "file": "reference/t_mush_test.clas.abap", + "description": "Own tests for {{P}}MUSH_HARVEST" + } + ], + "craft_checks": [ + "method_length", + "internal_table_kind" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1122/generation.json b/tasks_gen/train/G1122/generation.json new file mode 100644 index 0000000..93e057e --- /dev/null +++ b/tasks_gen/train/G1122/generation.json @@ -0,0 +1,57 @@ +{ + "id": "G1122", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/porta_bill_old.prog.abap line 39: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1090 (similarity spec 0.57, rules 0.64, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.60, rules 0.61, names 0.50). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 2 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/dentlab_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/dentlab_old.prog.abap line 151: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 151: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "reference/dentlab_old.prog.abap line 157: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 157: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "reference/dentlab_old.prog.abap line 163: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 163: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "Too close to task G1090 (similarity spec 0.61, rules 0.65, names 0.25). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.60, rules 0.60, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 7, + "extra": 2 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/rescue_log_old.prog.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "Too close to task G0142 (similarity spec 0.56, rules 0.64, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1090 (similarity spec 0.55, rules 0.66, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.53, rules 0.60, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1122/hidden/ext_hidden.clas.abap b/tasks_gen/train/G1122/hidden/ext_hidden.clas.abap new file mode 100644 index 0000000..106eeaf --- /dev/null +++ b/tasks_gen/train/G1122/hidden/ext_hidden.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}ext_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_flag TYPE c LENGTH 1. + TYPES tt_site TYPE RANGE OF {{p}}exting-site. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_site TYPE tt_site OPTIONAL + iv_defect TYPE ty_flag OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_rows FOR TESTING. + METHODS sorted_by_urgency FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS date_bounds FOR TESTING. + METHODS site_filter FOR TESTING. + METHODS defect_only FOR TESTING. + METHODS co2_replace FOR TESTING. + METHODS refill_boundary FOR TESTING. + METHODS pressure_service FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ext_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}ext_check_old + WITH p_from = iv_from + WITH p_to = iv_to + WITH s_site IN it_site + WITH p_defect = iv_defect + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_rows. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' ). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `REPLACE` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `7` act = value_of( ir_data = lr_data iv_row = 7 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = value_of( ir_data = lr_data iv_row = 7 iv_col = `STATE` ) ). + ENDMETHOD. + + METHOD sorted_by_urgency. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `5` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `6` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 5 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `URGENCY` ) ). + cl_abap_unit_assert=>assert_equals( exp = `4` act = value_of( ir_data = lr_data iv_row = 7 iv_col = `URGENCY` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXT_ID` ) ). + ENDMETHOD. + + METHOD date_bounds. + DATA(lr_data) = run( iv_from = '20251228' iv_to = '20251228' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `7` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + ENDMETHOD. + + METHOD site_filter. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' + it_site = VALUE #( ( sign = 'I' option = 'EQ' low = 'W2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `SHORT` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `4` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `STATE` ) ). + ENDMETHOD. + + METHOD defect_only. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' iv_defect = 'X' ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 5 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `SHORT` act = value_of( ir_data = lr_data iv_row = 5 iv_col = `STATE` ) ). + ENDMETHOD. + + METHOD co2_replace. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' + it_site = VALUE #( ( sign = 'I' option = 'EQ' low = 'W1' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `CO2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_TYPE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3500` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WEIGHT_G` ) ). + cl_abap_unit_assert=>assert_equals( exp = `REPLACE` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `SERVICE` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `8` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `PRESSURE_BAR` ) ). + ENDMETHOD. + + METHOD refill_boundary. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' + it_site = VALUE #( ( sign = 'I' option = 'EQ' low = 'W3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `5` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3999` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WEIGHT_G` ) ). + cl_abap_unit_assert=>assert_equals( exp = `REFILL` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `7` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXT_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `4000` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `WEIGHT_G` ) ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `STATE` ) ). + ENDMETHOD. + + METHOD pressure_service. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260331' + it_site = VALUE #( ( sign = 'I' option = 'EQ' low = 'W4' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `9` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PRESSURE_BAR` ) ). + cl_abap_unit_assert=>assert_equals( exp = `SERVICE` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STATE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `URGENCY` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1122/mutation.json b/tasks_gen/train/G1122/mutation.json new file mode 100644 index 0000000..89ff497 --- /dev/null +++ b/tasks_gen/train/G1122/mutation.json @@ -0,0 +1,39 @@ +{ + "task": "T15", + "mutants": [ + { + "object": "{{P}}EXT_CHECK_OLD", + "mutant": "weight threshold: < 4000 -> <= 4000 (rel)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "refill_boundary" + ] + }, + { + "object": "{{P}}EXT_CHECK_OLD", + "mutant": "sort: urgency ASCENDING -> DESCENDING (sort)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "sorted_by_urgency", + "site_filter", + "defect_only", + "date_filter" + ] + }, + { + "object": "{{P}}EXT_CHECK_OLD", + "mutant": "defect filter: urgency = 4 -> urgency <> 4 (logic)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "defect_only" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1122/reference/ext_check_old.prog.abap b/tasks_gen/train/G1122/reference/ext_check_old.prog.abap new file mode 100644 index 0000000..e8f5442 --- /dev/null +++ b/tasks_gen/train/G1122/reference/ext_check_old.prog.abap @@ -0,0 +1,209 @@ +REPORT {{p}}ext_check_old. + +DATA gv_site TYPE {{p}}exting-site. + +PARAMETERS: p_from TYPE d OBLIGATORY, + p_to TYPE d OBLIGATORY. + +SELECT-OPTIONS: s_site FOR gv_site. + +PARAMETERS p_defect AS CHECKBOX. + +CLASS lcl_check DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_state TYPE c LENGTH 7. + TYPES ty_urgency TYPE i. + TYPES: + BEGIN OF ty_line, + ext_id TYPE {{p}}exting-ext_id, + site TYPE {{p}}exting-site, + ext_type TYPE {{p}}exting-ext_type, + weight_g TYPE i, + pressure_bar TYPE i, + state TYPE ty_state, + urgency TYPE ty_urgency, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_site TYPE RANGE OF {{p}}exting-site. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_site TYPE tt_site + iv_defect_only TYPE abap_bool + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES tt_ext TYPE STANDARD TABLE OF {{p}}exting WITH EMPTY KEY. + METHODS select_checks + IMPORTING iv_from TYPE d + iv_to TYPE d + it_site TYPE tt_site + RETURNING VALUE(rt_ext) TYPE tt_ext. + METHODS to_line + IMPORTING is_ext TYPE {{p}}exting + RETURNING VALUE(rs_line) TYPE ty_line. + METHODS state_of + IMPORTING is_ext TYPE {{p}}exting + RETURNING VALUE(rv_state) TYPE ty_state. + METHODS urgency_of + IMPORTING is_ext TYPE {{p}}exting + RETURNING VALUE(rv_urgency) TYPE ty_urgency. +ENDCLASS. + +CLASS lcl_check IMPLEMENTATION. + METHOD collect. + DATA lt_lines TYPE tt_line. + DATA lt_ext TYPE tt_ext. + DATA ls_ext TYPE {{p}}exting. + lt_ext = select_checks( iv_from = iv_from iv_to = iv_to it_site = it_site ). + LOOP AT lt_ext INTO ls_ext. + IF iv_defect_only = abap_true AND urgency_of( ls_ext ) = 4. + CONTINUE. + ENDIF. + APPEND to_line( ls_ext ) TO lt_lines. + ENDLOOP. + SORT lt_lines BY urgency ASCENDING ext_id ASCENDING. + rt_lines = lt_lines. + ENDMETHOD. + + METHOD select_checks. + SELECT ext_id, site, ext_type, weight_g, pressure_bar, last_check, next_check + FROM {{p}}exting + WHERE last_check >= @iv_from + AND last_check <= @iv_to + AND site IN @it_site + INTO TABLE @rt_ext. + ENDMETHOD. + + METHOD to_line. + rs_line-ext_id = is_ext-ext_id. + rs_line-site = is_ext-site. + rs_line-ext_type = is_ext-ext_type. + rs_line-weight_g = is_ext-weight_g. + rs_line-pressure_bar = is_ext-pressure_bar. + rs_line-state = state_of( is_ext ). + rs_line-urgency = urgency_of( is_ext ). + ENDMETHOD. + + METHOD state_of. + IF is_ext-weight_g < 4000. + IF is_ext-ext_type = 'CO2'. + rv_state = 'REPLACE'. + ELSE. + rv_state = 'REFILL'. + ENDIF. + ELSEIF is_ext-pressure_bar < 10. + rv_state = 'SERVICE'. + ELSEIF is_ext-next_check - is_ext-last_check < 300. + rv_state = 'SHORT'. + ELSE. + rv_state = 'OK'. + ENDIF. + ENDMETHOD. + + METHOD urgency_of. + CASE state_of( is_ext ). + WHEN 'REPLACE' OR 'REFILL'. + rv_urgency = 1. + WHEN 'SERVICE'. + rv_urgency = 2. + WHEN 'SHORT'. + rv_urgency = 3. + WHEN OTHERS. + rv_urgency = 4. + ENDCASE. + ENDMETHOD. + + METHOD display. + DATA lo_alv TYPE REF TO cl_salv_table. + DATA lx_error TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lx_error. + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA lv_defect_only TYPE abap_bool. + DATA go_check TYPE REF TO lcl_check. + DATA gt_lines TYPE lcl_check=>tt_line. + IF p_defect = 'X'. + lv_defect_only = abap_true. + ENDIF. + CREATE OBJECT go_check. + gt_lines = go_check->collect( iv_from = p_from + iv_to = p_to + it_site = s_site[] + iv_defect_only = lv_defect_only ). + go_check->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_check DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS states_in_order FOR TESTING. + METHODS defect_filter FOR TESTING. + METHODS date_range FOR TESTING. +ENDCLASS. + +CLASS ltc_check IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}EXTING' ) ) ). + DATA lt_ext TYPE STANDARD TABLE OF {{p}}exting WITH EMPTY KEY. + lt_ext = VALUE #( + ( ext_id = 1 site = 'A' ext_type = 'CO2' weight_g = 3000 pressure_bar = 15 last_check = '20260101' next_check = '20270101' ) + ( ext_id = 2 site = 'A' ext_type = 'PW' weight_g = 6000 pressure_bar = 8 last_check = '20260102' next_check = '20270102' ) + ( ext_id = 3 site = 'B' ext_type = 'PW' weight_g = 5000 pressure_bar = 12 last_check = '20260103' next_check = '20260120' ) + ( ext_id = 4 site = 'B' ext_type = 'PW' weight_g = 5000 pressure_bar = 12 last_check = '20260104' next_check = '20270104' ) ). + go_env->insert_test_data( lt_ext ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD states_in_order. + DATA lo_check TYPE REF TO lcl_check. + DATA lt_lines TYPE lcl_check=>tt_line. + CREATE OBJECT lo_check. + lt_lines = lo_check->collect( iv_from = '20260101' + iv_to = '20261231' + it_site = VALUE #( ) + iv_defect_only = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'REPLACE' act = lt_lines[ 1 ]-state ). + cl_abap_unit_assert=>assert_equals( exp = 'SERVICE' act = lt_lines[ 2 ]-state ). + cl_abap_unit_assert=>assert_equals( exp = 'SHORT' act = lt_lines[ 3 ]-state ). + cl_abap_unit_assert=>assert_equals( exp = 'OK' act = lt_lines[ 4 ]-state ). + ENDMETHOD. + + METHOD defect_filter. + DATA lo_check TYPE REF TO lcl_check. + DATA lt_lines TYPE lcl_check=>tt_line. + CREATE OBJECT lo_check. + lt_lines = lo_check->collect( iv_from = '20260101' + iv_to = '20261231' + it_site = VALUE #( ) + iv_defect_only = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'SHORT' act = lt_lines[ 3 ]-state ). + ENDMETHOD. + + METHOD date_range. + DATA lo_check TYPE REF TO lcl_check. + DATA lt_lines TYPE lcl_check=>tt_line. + CREATE OBJECT lo_check. + lt_lines = lo_check->collect( iv_from = '20260103' + iv_to = '20260103' + it_site = VALUE #( ) + iv_defect_only = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-ext_id ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1122/seed/ext_check_old.prog.abap b/tasks_gen/train/G1122/seed/ext_check_old.prog.abap new file mode 100644 index 0000000..aea3050 --- /dev/null +++ b/tasks_gen/train/G1122/seed/ext_check_old.prog.abap @@ -0,0 +1,80 @@ +REPORT {{p}}ext_check_old. + +DATA gv_site TYPE {{p}}exting-site. + +PARAMETERS: p_from TYPE d OBLIGATORY, + p_to TYPE d OBLIGATORY. + +SELECT-OPTIONS: s_site FOR gv_site. + +PARAMETERS p_defect AS CHECKBOX. + +TYPES: BEGIN OF ty_line, + ext_id TYPE {{p}}exting-ext_id, + site TYPE {{p}}exting-site, + ext_type TYPE {{p}}exting-ext_type, + weight_g TYPE i, + pressure_bar TYPE i, + state TYPE c LENGTH 7, + urgency TYPE i, + END OF ty_line. + +DATA: gt_lines TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY, + gs_line TYPE ty_line, + lt_ext TYPE STANDARD TABLE OF {{p}}exting, + ls_ext TYPE {{p}}exting, + lv_state TYPE c LENGTH 7, + lv_prio TYPE i, + lo_alv TYPE REF TO cl_salv_table. + +START-OF-SELECTION. + + SELECT * FROM {{p}}exting INTO TABLE lt_ext. + + LOOP AT lt_ext INTO ls_ext. + IF ls_ext-last_check >= p_from AND ls_ext-last_check <= p_to + AND ls_ext-site IN s_site. + IF ls_ext-weight_g < 4000. + IF ls_ext-ext_type = 'CO2'. + lv_state = 'REPLACE'. + ELSE. + lv_state = 'REFILL'. + ENDIF. + lv_prio = 1. + ELSEIF ls_ext-pressure_bar < 10. + lv_state = 'SERVICE'. + lv_prio = 2. + ELSEIF ls_ext-next_check - ls_ext-last_check < 300. + lv_state = 'SHORT'. + lv_prio = 3. + ELSE. + lv_state = 'OK'. + lv_prio = 4. + ENDIF. + IF p_defect = 'X' AND lv_prio = 4. + CONTINUE. + ENDIF. + CLEAR gs_line. + gs_line-ext_id = ls_ext-ext_id. + gs_line-site = ls_ext-site. + gs_line-ext_type = ls_ext-ext_type. + gs_line-weight_g = ls_ext-weight_g. + gs_line-pressure_bar = ls_ext-pressure_bar. + gs_line-state = lv_state. + gs_line-urgency = lv_prio. + APPEND gs_line TO gt_lines. + ENDIF. + ENDLOOP. + + SORT gt_lines BY urgency ASCENDING ext_id ASCENDING. + + PERFORM display_alv. + +FORM display_alv. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv + CHANGING t_table = gt_lines ). + lo_alv->display( ). + CATCH cx_salv_msg. + ENDTRY. +ENDFORM. diff --git a/tasks_gen/train/G1122/seed/ext_seed.clas.abap b/tasks_gen/train/G1122/seed/ext_seed.clas.abap new file mode 100644 index 0000000..91e2ba5 --- /dev/null +++ b/tasks_gen/train/G1122/seed/ext_seed.clas.abap @@ -0,0 +1,19 @@ +CLASS {{p}}ext_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}ext_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}exting. + INSERT {{p}}exting FROM TABLE @( VALUE #( + ( ext_id = 1 site = 'W1' ext_type = 'CO2' weight_g = 3500 pressure_bar = 15 last_check = '20260105' next_check = '20270105' ) + ( ext_id = 2 site = 'W1' ext_type = 'PW' weight_g = 6000 pressure_bar = 8 last_check = '20260120' next_check = '20270120' ) + ( ext_id = 3 site = 'W2' ext_type = 'PW' weight_g = 9000 pressure_bar = 14 last_check = '20260201' next_check = '20260501' ) + ( ext_id = 4 site = 'W2' ext_type = 'CO2' weight_g = 5000 pressure_bar = 10 last_check = '20260215' next_check = '20270215' ) + ( ext_id = 5 site = 'W3' ext_type = 'PW' weight_g = 3999 pressure_bar = 13 last_check = '20260301' next_check = '20270301' ) + ( ext_id = 6 site = 'W4' ext_type = 'CO2' weight_g = 7000 pressure_bar = 9 last_check = '20251220' next_check = '20261220' ) + ( ext_id = 7 site = 'W3' ext_type = 'PW' weight_g = 4000 pressure_bar = 11 last_check = '20251228' next_check = '20261024' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1122/seed/exting.tabl.asabap b/tasks_gen/train/G1122/seed/exting.tabl.asabap new file mode 100644 index 0000000..f14cc37 --- /dev/null +++ b/tasks_gen/train/G1122/seed/exting.tabl.asabap @@ -0,0 +1,15 @@ +@EndUserText.label : 'Fire extinguishers' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}exting { + key client : abap.clnt not null; + key ext_id : abap.int4 not null; + site : abap.char(6); + ext_type : abap.char(2); + weight_g : abap.int4; + pressure_bar : abap.int4; + last_check : abap.dats; + next_check : abap.dats; +} diff --git a/tasks_gen/train/G1122/spec.md b/tasks_gen/train/G1122/spec.md new file mode 100644 index 0000000..c501614 --- /dev/null +++ b/tasks_gen/train/G1122/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A safety-services company inspects fire extinguishers at customer sites. The +inspection office runs the report {{P}}EXT_CHECK_OLD in package $TMP to see which +extinguishers need attention. The report works, but the source is a legacy +tangle. Rewrite the source so that a colleague can read it and change it. The +observable behavior of the report must not change. + +# 2. Open questions +None. + +# 3. Context +- {{P}}EXT_CHECK_OLD is an executable program in package $TMP. It is active. +- {{P}}EXTING is a database table in package $TMP with the inspection master + data: EXT_ID (INT4, key), SITE (CHAR 6), EXT_TYPE (CHAR 2), WEIGHT_G (INT4), + PRESSURE_BAR (INT4), LAST_CHECK (DATS), NEXT_CHECK (DATS). +- The program reads the table and writes one output line for each extinguisher. + +# 4. Contract +- Keep the program name {{P}}EXT_CHECK_OLD and the package $TMP. +- Keep the selection screen unchanged: + - P_FROM, type DATS, obligatory. + - P_TO, type DATS, obligatory. + - S_SITE, select-option for SITE. + - P_DEFECT, checkbox. +- The output is an ALV list (CL_SALV_TABLE). One output line has the columns + EXT_ID, SITE, EXT_TYPE, WEIGHT_G, PRESSURE_BAR, STATE and URGENCY. +- No other public name of the program changes. + +# 5. Business rules +The present source of {{P}}EXT_CHECK_OLD defines the rules. Read that source +before you change anything. Every rule stays in force: the selection logic, the +special cases, the values of the columns STATE and URGENCY, and the order of the +output lines. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Split the logic into small methods with clear + names. No method has 40 statements or more. Read the database with a single + SELECT. Do not use obsolete language elements. +- Out of scope: do not change the table {{P}}EXTING and the seed class + {{P}}EXT_SEED. + +# 7. Acceptance +- The program is active and free of syntax errors. +- The output is identical to the output before the change. +- The hidden tests pass. +- Add ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1122/task.json b/tasks_gen/train/G1122/task.json new file mode 100644 index 0000000..f4008b9 --- /dev/null +++ b/tasks_gen/train/G1122/task.json @@ -0,0 +1,70 @@ +{ + "id": "G1122", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}EXTING", + "file": "seed/exting.tabl.asabap", + "description": "Fire extinguishers and their inspection data" + }, + { + "type": "PROG", + "name": "{{P}}EXT_CHECK_OLD", + "file": "seed/ext_check_old.prog.abap", + "description": "Legacy inspection report of the safety-services company" + }, + { + "type": "CLAS", + "name": "{{P}}EXT_SEED", + "file": "seed/ext_seed.clas.abap", + "description": "Seed data for the fire extinguishers", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}EXT_CHECK_OLD", + "parameters": [ + "p_from", + "p_to", + "s_site", + "p_defect" + ] + } + ], + "out_of_scope": [ + "{{P}}EXTING", + "{{P}}EXT_SEED" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}EXT_HIDDEN", + "file": "hidden/ext_hidden.clas.abap", + "description": "Hidden tests for the refactored inspection report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}EXT_CHECK_OLD", + "file": "reference/ext_check_old.prog.abap", + "description": "Refactored inspection report" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1123/generation.json b/tasks_gen/train/G1123/generation.json new file mode 100644 index 0000000..706493b --- /dev/null +++ b/tasks_gen/train/G1123/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1123", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 89.2, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 8619 (char 8618)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1123/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1123/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..145bba8 --- /dev/null +++ b/tasks_gen/train/G1123/hidden/t02_hidden.clas.abap @@ -0,0 +1,142 @@ +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}}airport_fee. + METHODS setup. + METHODS assert_fee + IMPORTING iv_exp TYPE {{p}}airport_fee=>ty_amount + iv_act TYPE {{p}}airport_fee=>ty_amount. + METHODS light_zone1_eur FOR TESTING. + METHODS medium_zone2_night_eur FOR TESTING. + METHODS heavy_zone3_night_chf FOR TESTING. + METHODS light_zone2_eur FOR TESTING. + METHODS light_zone2_usd FOR TESTING. + METHODS unknown_zone_ignored FOR TESTING. + METHODS unknown_class_zero FOR TESTING. + METHODS unknown_curr_zero FOR TESTING. + METHODS zero_tonnes_zero FOR TESTING. + METHODS pax_zone2_eur FOR TESTING. + METHODS pax_zone1_chf FOR TESTING. + METHODS pax_invalid_zero FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}airport_fee( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( exp = iv_exp act = iv_act ). + ENDMETHOD. + + METHOD light_zone1_eur. + assert_fee( iv_exp = '125.00' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD medium_zone2_night_eur. + assert_fee( iv_exp = '701.20' + iv_act = mo_cut->landing_fee( iv_class = 'M' + iv_zone = 'Z2' + iv_night = 'N' + iv_currency = 'EUR' + iv_tonnes = 20 ) ). + ENDMETHOD. + + METHOD heavy_zone3_night_chf. + assert_fee( iv_exp = '1898.10' + iv_act = mo_cut->landing_fee( iv_class = 'H' + iv_zone = 'Z3' + iv_night = 'N' + iv_currency = 'CHF' + iv_tonnes = 30 ) ). + ENDMETHOD. + + METHOD light_zone2_eur. + assert_fee( iv_exp = '562.40' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z2' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 40 ) ). + ENDMETHOD. + + METHOD light_zone2_usd. + assert_fee( iv_exp = '646.80' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z2' + iv_night = 'D' + iv_currency = 'USD' + iv_tonnes = 40 ) ). + ENDMETHOD. + + METHOD unknown_zone_ignored. + assert_fee( iv_exp = '245.00' + iv_act = mo_cut->landing_fee( iv_class = 'M' + iv_zone = 'Z9' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD unknown_class_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->landing_fee( iv_class = 'X' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD unknown_curr_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->landing_fee( iv_class = 'M' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'GBP' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD zero_tonnes_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 0 ) ). + ENDMETHOD. + + METHOD pax_zone2_eur. + assert_fee( iv_exp = '1382.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z2' + iv_currency = 'EUR' + iv_passengers = 100 ) ). + ENDMETHOD. + + METHOD pax_zone1_chf. + assert_fee( iv_exp = '10.51' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z1' + iv_currency = 'CHF' + iv_passengers = 1 ) ). + ENDMETHOD. + + METHOD pax_invalid_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z1' + iv_currency = 'EUR' + iv_passengers = 0 ) ). + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z1' + iv_currency = 'EUR' + iv_passengers = -5 ) ). + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z1' + iv_currency = 'GBP' + iv_passengers = 5 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1123/reference/airport_fee.clas.abap b/tasks_gen/train/G1123/reference/airport_fee.clas.abap new file mode 100644 index 0000000..71da172 --- /dev/null +++ b/tasks_gen/train/G1123/reference/airport_fee.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}airport_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_class TYPE c LENGTH 1. + TYPES ty_zone TYPE c LENGTH 2. + TYPES ty_flag TYPE c LENGTH 1. + TYPES ty_curr TYPE c LENGTH 3. + TYPES ty_amount TYPE p LENGTH 10 DECIMALS 2. + + METHODS landing_fee + IMPORTING iv_class TYPE ty_class + iv_zone TYPE ty_zone + iv_night TYPE ty_flag + iv_currency TYPE ty_curr + iv_tonnes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS passenger_fee + IMPORTING iv_zone TYPE ty_zone + iv_currency TYPE ty_curr + iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + + PRIVATE SECTION. + TYPES ty_calc TYPE p LENGTH 13 DECIMALS 4. + + CONSTANTS: + c_night_per_tonne TYPE ty_amount VALUE '7.50', + c_pax_fee TYPE ty_amount VALUE '12.37', + c_full_percent TYPE ty_amount VALUE '100.00', + BEGIN OF c_rate, + eur TYPE ty_amount VALUE '1.00', + usd TYPE ty_amount VALUE '1.15', + chf TYPE ty_amount VALUE '0.85', + END OF c_rate. + + METHODS base_per_tonne + IMPORTING iv_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS zone_percent + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_percent) TYPE ty_amount. + METHODS zone_surcharge + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS convert + IMPORTING iv_amount TYPE ty_calc + iv_currency TYPE ty_curr + RETURNING VALUE(rv_fee) TYPE ty_calc. +ENDCLASS. + + +CLASS {{p}}airport_fee IMPLEMENTATION. + METHOD landing_fee. + IF iv_tonnes < 1. + RETURN. + ENDIF. + DATA lv_base TYPE ty_amount. + lv_base = base_per_tonne( iv_class ). + IF lv_base IS INITIAL. + RETURN. + ENDIF. + DATA lv_percent TYPE ty_amount. + lv_percent = c_full_percent + zone_percent( iv_zone ). + DATA lv_per_tonne TYPE ty_calc. + lv_per_tonne = lv_base * lv_percent / c_full_percent. + IF iv_night = 'N'. + lv_per_tonne = lv_per_tonne + c_night_per_tonne. + ENDIF. + DATA lv_converted TYPE ty_calc. + lv_converted = convert( iv_amount = lv_per_tonne iv_currency = iv_currency ). + rv_fee = lv_converted * iv_tonnes. + ENDMETHOD. + + METHOD passenger_fee. + IF iv_passengers < 1. + RETURN. + ENDIF. + DATA lv_per_pax TYPE ty_amount. + lv_per_pax = c_pax_fee + zone_surcharge( iv_zone ). + DATA lv_converted TYPE ty_calc. + lv_converted = convert( iv_amount = lv_per_pax iv_currency = iv_currency ). + rv_fee = lv_converted * iv_passengers. + ENDMETHOD. + + METHOD base_per_tonne. + CASE iv_class. + WHEN 'L'. + rv_fee = '12.50'. + WHEN 'M'. + rv_fee = '24.50'. + WHEN 'H'. + rv_fee = '52.50'. + ENDCASE. + ENDMETHOD. + + METHOD zone_percent. + CASE iv_zone. + WHEN 'Z2'. + rv_percent = '12.50'. + WHEN 'Z3'. + rv_percent = '27.50'. + ENDCASE. + ENDMETHOD. + + METHOD zone_surcharge. + CASE iv_zone. + WHEN 'Z2'. + rv_surcharge = '1.45'. + WHEN 'Z3'. + rv_surcharge = '2.90'. + ENDCASE. + ENDMETHOD. + + METHOD convert. + CASE iv_currency. + WHEN 'EUR'. + rv_fee = round( val = iv_amount * c_rate-eur dec = 2 ). + WHEN 'USD'. + rv_fee = round( val = iv_amount * c_rate-usd dec = 2 ). + WHEN 'CHF'. + rv_fee = round( val = iv_amount * c_rate-chf dec = 2 ). + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1123/reference/airport_fee.testclasses.abap b/tasks_gen/train/G1123/reference/airport_fee.testclasses.abap new file mode 100644 index 0000000..2cb4deb --- /dev/null +++ b/tasks_gen/train/G1123/reference/airport_fee.testclasses.abap @@ -0,0 +1,95 @@ +CLASS ltc_airport_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}airport_fee. + METHODS setup. + METHODS assert_fee + IMPORTING iv_exp TYPE {{p}}airport_fee=>ty_amount + iv_act TYPE {{p}}airport_fee=>ty_amount. + METHODS light_zone1_eur FOR TESTING. + METHODS medium_zone2_night_eur FOR TESTING. + METHODS heavy_zone3_night_chf FOR TESTING. + METHODS light_zone2_eur FOR TESTING. + METHODS unknown_class_zero FOR TESTING. + METHODS unknown_curr_zero FOR TESTING. + METHODS pax_zone2_eur FOR TESTING. + METHODS zero_pax_zero FOR TESTING. +ENDCLASS. + + +CLASS ltc_airport_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}airport_fee( ). + ENDMETHOD. + + METHOD assert_fee. + cl_abap_unit_assert=>assert_equals( exp = iv_exp act = iv_act ). + ENDMETHOD. + + METHOD light_zone1_eur. + assert_fee( iv_exp = '125.00' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD medium_zone2_night_eur. + assert_fee( iv_exp = '701.20' + iv_act = mo_cut->landing_fee( iv_class = 'M' + iv_zone = 'Z2' + iv_night = 'N' + iv_currency = 'EUR' + iv_tonnes = 20 ) ). + ENDMETHOD. + + METHOD heavy_zone3_night_chf. + assert_fee( iv_exp = '1898.10' + iv_act = mo_cut->landing_fee( iv_class = 'H' + iv_zone = 'Z3' + iv_night = 'N' + iv_currency = 'CHF' + iv_tonnes = 30 ) ). + ENDMETHOD. + + METHOD light_zone2_eur. + assert_fee( iv_exp = '562.40' + iv_act = mo_cut->landing_fee( iv_class = 'L' + iv_zone = 'Z2' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 40 ) ). + ENDMETHOD. + + METHOD unknown_class_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->landing_fee( iv_class = 'X' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'EUR' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD unknown_curr_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->landing_fee( iv_class = 'M' + iv_zone = 'Z1' + iv_night = 'D' + iv_currency = 'GBP' + iv_tonnes = 10 ) ). + ENDMETHOD. + + METHOD pax_zone2_eur. + assert_fee( iv_exp = '1382.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z2' + iv_currency = 'EUR' + iv_passengers = 100 ) ). + ENDMETHOD. + + METHOD zero_pax_zero. + assert_fee( iv_exp = '0.00' + iv_act = mo_cut->passenger_fee( iv_zone = 'Z1' + iv_currency = 'EUR' + iv_passengers = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1123/spec.md b/tasks_gen/train/G1123/spec.md new file mode 100644 index 0000000..d2f60bb --- /dev/null +++ b/tasks_gen/train/G1123/spec.md @@ -0,0 +1,78 @@ +# 1. Goal +An airport charges a landing fee and a passenger fee for each aircraft movement. +The operations desk calls the calculation before it issues the invoice. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}AIRPORT_FEE is new. It is in package $TMP. +- The airport uses short codes. A weight class has one character. A noise zone + has two characters. A night flag has one character. A currency has three + characters. +- The landing fee is charged for each tonne of the maximum take-off mass. + +# 4. Contract +- Create the class {{P}}AIRPORT_FEE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Define these public types: + - TY_CLASS TYPE c LENGTH 1 + - TY_ZONE TYPE c LENGTH 2 + - TY_FLAG TYPE c LENGTH 1 + - TY_CURR TYPE c LENGTH 3 + - TY_AMOUNT TYPE p LENGTH 10 DECIMALS 2 +- Define these public methods: + - LANDING_FEE + IMPORTING iv_class TYPE ty_class + iv_zone TYPE ty_zone + iv_night TYPE ty_flag + iv_currency TYPE ty_curr + iv_tonnes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount + - PASSENGER_FEE + IMPORTING iv_zone TYPE ty_zone + iv_currency TYPE ty_curr + iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount +- Do not add other public methods. + +# 5. Business rules + +## 5.1 Landing fee +Apply the rules in this sequence. +1. If iv_currency is not EUR, USD or CHF, return 0.00. +2. If iv_class is not L, M or H, return 0.00. +3. If iv_tonnes is less than 1, return 0.00. +4. The base fee for one tonne is 12.50 EUR for L, 24.50 EUR for M and 52.50 EUR + for H. +5. The noise zone gives a surcharge on the base fee for one tonne. Z1 gives + 0 percent, Z2 gives 12.5 percent and Z3 gives 27.5 percent. Any other zone + gives 0 percent. +6. If iv_night is N, add 7.50 EUR to the fee for one tonne. +7. Convert the fee for one tonne with the currency rate and round it to + 2 decimals. EUR is 1.00, USD is 1.15 and CHF is 0.85. +8. Multiply the rounded fee for one tonne by iv_tonnes. The result is the + landing fee. + +## 5.2 Passenger fee +Apply the rules in this sequence. +1. If iv_currency is not EUR, USD or CHF, return 0.00. +2. If iv_passengers is less than 1, return 0.00. +3. The fee for one passenger is 12.37 EUR. +4. The noise zone gives a surcharge for each passenger. Z1 gives 0.00 EUR, Z2 + gives 1.45 EUR and Z3 gives 2.90 EUR. Any other zone gives 0.00 EUR. +5. Convert the fee for one passenger with the currency rate and round it to + 2 decimals. EUR is 1.00, USD is 1.15 and CHF is 0.85. +6. Multiply the rounded fee for one passenger by iv_passengers. The result is + the passenger fee. + +# 6. Constraints +- Release target: SAP_BASIS 8.16 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. +- Out of scope: no database access, no ALV list, no CDS view. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1123/task.json b/tasks_gen/train/G1123/task.json new file mode 100644 index 0000000..221ad77 --- /dev/null +++ b/tasks_gen/train/G1123/task.json @@ -0,0 +1,41 @@ +{ + "id": "G1123", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}AIRPORT_FEE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the airport fee class" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}AIRPORT_FEE", + "file": "reference/airport_fee.clas.abap", + "description": "Airport landing fee and passenger fee calculator", + "testclasses_file": "reference/airport_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1124/faulty/m0_shift_booker.clas.abap b/tasks_gen/train/G1124/faulty/m0_shift_booker.clas.abap new file mode 100644 index 0000000..b7f9458 --- /dev/null +++ b/tasks_gen/train/G1124/faulty/m0_shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate - is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours <= 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode <> 'OT1' + AND is_shift-calc_mode <> 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + AND is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/faulty/m1_shift_booker.clas.abap b/tasks_gen/train/G1124/faulty/m1_shift_booker.clas.abap new file mode 100644 index 0000000..fb8ff42 --- /dev/null +++ b/tasks_gen/train/G1124/faulty/m1_shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate + is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours > 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode <> 'OT1' + AND is_shift-calc_mode <> 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + AND is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/faulty/m2_shift_booker.clas.abap b/tasks_gen/train/G1124/faulty/m2_shift_booker.clas.abap new file mode 100644 index 0000000..5b6b02d --- /dev/null +++ b/tasks_gen/train/G1124/faulty/m2_shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate + is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours <= 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode = 'OT1' + AND is_shift-calc_mode <> 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + AND is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/faulty/m3_shift_booker.clas.abap b/tasks_gen/train/G1124/faulty/m3_shift_booker.clas.abap new file mode 100644 index 0000000..9e4d81e --- /dev/null +++ b/tasks_gen/train/G1124/faulty/m3_shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate + is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours <= 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode <> 'OT1' + AND is_shift-calc_mode = 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + AND is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/faulty/m4_shift_booker.clas.abap b/tasks_gen/train/G1124/faulty/m4_shift_booker.clas.abap new file mode 100644 index 0000000..c65dc7d --- /dev/null +++ b/tasks_gen/train/G1124/faulty/m4_shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate + is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours <= 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode <> 'OT1' + AND is_shift-calc_mode <> 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + OR is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/generation.json b/tasks_gen/train/G1124/generation.json new file mode 100644 index 0000000..7f0c950 --- /dev/null +++ b/tasks_gen/train/G1124/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1124", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1124/hidden/d02_hidden.clas.abap b/tasks_gen/train/G1124/hidden/d02_hidden.clas.abap new file mode 100644 index 0000000..31091f7 --- /dev/null +++ b/tasks_gen/train/G1124/hidden/d02_hidden.clas.abap @@ -0,0 +1,139 @@ +CLASS {{p}}d02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + DATA mo_cut TYPE REF TO {{p}}shift_booker. + + METHODS setup. + METHODS valid_shift + RETURNING VALUE(rs_shift) TYPE {{p}}shift_booker=>ty_shift. + METHODS expect_reason + IMPORTING is_shift TYPE {{p}}shift_booker=>ty_shift + iv_reason TYPE ty_reason. + METHODS expect_pay + IMPORTING is_shift TYPE {{p}}shift_booker=>ty_shift + iv_pay TYPE {{p}}shift_booker=>ty_amount. + + METHODS books_standard_shift FOR TESTING. + METHODS books_overtime_shift FOR TESTING. + METHODS books_second_overtime FOR TESTING. + METHODS books_small_shift FOR TESTING. + METHODS rejects_empty_order FOR TESTING. + METHODS rejects_initial_date FOR TESTING. + METHODS rejects_zero_hours FOR TESTING. + METHODS rejects_unknown_mode FOR TESTING. + METHODS rejects_unknown_group FOR TESTING. + METHODS rejects_zero_pieces FOR TESTING. + METHODS order_rule_wins FOR TESTING. +ENDCLASS. + + +CLASS {{p}}d02_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}shift_booker( ). + ENDMETHOD. + + METHOD valid_shift. + rs_shift = VALUE #( order_no = '4711' + start_date = '20240212' + work_hours = '8.00' + calc_mode = 'STD' + group_code = 'A' + piece_count = 100 ). + ENDMETHOD. + + METHOD expect_reason. + TRY. + mo_cut->evaluate( is_shift ). + cl_abap_unit_assert=>fail( msg = |no exception for { iv_reason }| ). + CATCH {{p}}cx_shift_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = iv_reason + act = lx_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD expect_pay. + TRY. + cl_abap_unit_assert=>assert_equals( exp = iv_pay + act = mo_cut->evaluate( is_shift ) ). + CATCH {{p}}cx_shift_error. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD books_standard_shift. + expect_pay( is_shift = valid_shift( ) iv_pay = '194.00' ). + ENDMETHOD. + + METHOD books_overtime_shift. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '7.50'. + ls_shift-calc_mode = 'OT1'. + ls_shift-group_code = 'B'. + ls_shift-piece_count = 40. + expect_pay( is_shift = ls_shift iv_pay = '267.50' ). + ENDMETHOD. + + METHOD books_second_overtime. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '6.00'. + ls_shift-calc_mode = 'OT2'. + ls_shift-group_code = 'C'. + ls_shift-piece_count = 10. + expect_pay( is_shift = ls_shift iv_pay = '317.00' ). + ENDMETHOD. + + METHOD books_small_shift. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '0.50'. + ls_shift-piece_count = 1. + expect_pay( is_shift = ls_shift iv_pay = '9.50' ). + ENDMETHOD. + + METHOD rejects_empty_order. + DATA(ls_shift) = valid_shift( ). + ls_shift-order_no = ''. + expect_reason( is_shift = ls_shift iv_reason = 'ORDER' ). + ENDMETHOD. + + METHOD rejects_initial_date. + DATA(ls_shift) = valid_shift( ). + ls_shift-start_date = '00000000'. + expect_reason( is_shift = ls_shift iv_reason = 'DATE' ). + ENDMETHOD. + + METHOD rejects_zero_hours. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '0.00'. + expect_reason( is_shift = ls_shift iv_reason = 'HOURS' ). + ENDMETHOD. + + METHOD rejects_unknown_mode. + DATA(ls_shift) = valid_shift( ). + ls_shift-calc_mode = 'XXX'. + expect_reason( is_shift = ls_shift iv_reason = 'MODE' ). + ENDMETHOD. + + METHOD rejects_unknown_group. + DATA(ls_shift) = valid_shift( ). + ls_shift-group_code = 'Z'. + expect_reason( is_shift = ls_shift iv_reason = 'GROUP' ). + ENDMETHOD. + + METHOD rejects_zero_pieces. + DATA(ls_shift) = valid_shift( ). + ls_shift-piece_count = 0. + expect_reason( is_shift = ls_shift iv_reason = 'COUNT' ). + ENDMETHOD. + + METHOD order_rule_wins. + DATA(ls_shift) = valid_shift( ). + ls_shift-order_no = ''. + ls_shift-work_hours = '0.00'. + expect_reason( is_shift = ls_shift iv_reason = 'ORDER' ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1124/mutation.json b/tasks_gen/train/G1124/mutation.json new file mode 100644 index 0000000..adfdc30 --- /dev/null +++ b/tasks_gen/train/G1124/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1124", + "mutants": [ + { + "object": "{{P}}SHIFT_BOOKER", + "mutant": "line 49: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "BOOKS_OVERTIME_SHIFT", + "BOOKS_SECOND_OVERTIME", + "BOOKS_SMALL_SHIFT", + "BOOKS_STANDARD_SHIFT" + ] + }, + { + "object": "{{P}}SHIFT_BOOKER", + "mutant": "line 59: <= -> > (rel)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "BOOKS_OVERTIME_SHIFT", + "BOOKS_SECOND_OVERTIME", + "BOOKS_SMALL_SHIFT", + "BOOKS_STANDARD_SHIFT", + "REJECTS_UNKNOWN_GROUP", + "REJECTS_UNKNOWN_MODE", + "REJECTS_ZERO_HOURS", + "REJECTS_ZERO_PIECES" + ] + }, + { + "object": "{{P}}SHIFT_BOOKER", + "mutant": "line 63: <> -> = (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BOOKS_OVERTIME_SHIFT", + "REJECTS_UNKNOWN_MODE" + ] + }, + { + "object": "{{P}}SHIFT_BOOKER", + "mutant": "line 64: <> -> = (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BOOKS_SECOND_OVERTIME", + "REJECTS_UNKNOWN_MODE" + ] + }, + { + "object": "{{P}}SHIFT_BOOKER", + "mutant": "line 68: AND -> OR (logic)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "BOOKS_OVERTIME_SHIFT", + "BOOKS_SECOND_OVERTIME", + "BOOKS_SMALL_SHIFT", + "BOOKS_STANDARD_SHIFT", + "REJECTS_ZERO_PIECES" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1124/reference/cx_shift_error.clas.abap b/tasks_gen/train/G1124/reference/cx_shift_error.clas.abap new file mode 100644 index 0000000..965825a --- /dev/null +++ b/tasks_gen/train/G1124/reference/cx_shift_error.clas.abap @@ -0,0 +1,9 @@ +CLASS {{p}}cx_shift_error DEFINITION PUBLIC INHERITING FROM cx_static_check + FINAL CREATE PUBLIC. + PUBLIC SECTION. + DATA mv_reason TYPE c LENGTH 10. +ENDCLASS. + + +CLASS {{p}}cx_shift_error IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1124/reference/shift_booker.clas.abap b/tasks_gen/train/G1124/reference/shift_booker.clas.abap new file mode 100644 index 0000000..1389fa8 --- /dev/null +++ b/tasks_gen/train/G1124/reference/shift_booker.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}shift_booker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_hours TYPE p LENGTH 5 DECIMALS 2. + TYPES ty_mode TYPE c LENGTH 3. + TYPES ty_group TYPE c LENGTH 1. + TYPES ty_order TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_shift, + order_no TYPE ty_order, + start_date TYPE d, + work_hours TYPE ty_hours, + calc_mode TYPE ty_mode, + group_code TYPE ty_group, + piece_count TYPE i, + END OF ty_shift. + + METHODS evaluate + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{p}}cx_shift_error. + + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS c_piece_bonus TYPE ty_amount VALUE '0.50'. + + METHODS check_shift + IMPORTING is_shift TYPE ty_shift + RAISING {{p}}cx_shift_error. + METHODS raise_error + IMPORTING iv_reason TYPE ty_reason + RAISING {{p}}cx_shift_error. + METHODS base_rate + IMPORTING iv_group_code TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS mode_factor + IMPORTING iv_calc_mode TYPE ty_mode + RETURNING VALUE(rv_factor) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}shift_booker IMPLEMENTATION. + METHOD evaluate. + check_shift( is_shift ). + DATA lv_hour_rate TYPE ty_amount. + lv_hour_rate = base_rate( is_shift-group_code ) * mode_factor( is_shift-calc_mode ). + rv_pay = is_shift-work_hours * lv_hour_rate + is_shift-piece_count * c_piece_bonus. + ENDMETHOD. + + METHOD check_shift. + IF is_shift-order_no IS INITIAL. + raise_error( 'ORDER' ). + ENDIF. + IF is_shift-start_date IS INITIAL. + raise_error( 'DATE' ). + ENDIF. + IF is_shift-work_hours <= 0. + raise_error( 'HOURS' ). + ENDIF. + IF is_shift-calc_mode <> 'STD' + AND is_shift-calc_mode <> 'OT1' + AND is_shift-calc_mode <> 'OT2'. + raise_error( 'MODE' ). + ENDIF. + IF is_shift-group_code <> 'A' + AND is_shift-group_code <> 'B' + AND is_shift-group_code <> 'C'. + raise_error( 'GROUP' ). + ENDIF. + IF is_shift-piece_count < 1. + raise_error( 'COUNT' ). + ENDIF. + ENDMETHOD. + + METHOD raise_error. + DATA(lx_error) = NEW {{p}}cx_shift_error( ). + lx_error->mv_reason = iv_reason. + RAISE EXCEPTION lx_error. + ENDMETHOD. + + METHOD base_rate. + CASE iv_group_code. + WHEN 'A'. + rv_rate = '18.00'. + WHEN 'B'. + rv_rate = '22.00'. + WHEN 'C'. + rv_rate = '26.00'. + WHEN OTHERS. + rv_rate = '0.00'. + ENDCASE. + ENDMETHOD. + + METHOD mode_factor. + CASE iv_calc_mode. + WHEN 'STD'. + rv_factor = '1.00'. + WHEN 'OT1'. + rv_factor = '1.50'. + WHEN 'OT2'. + rv_factor = '2.00'. + WHEN OTHERS. + rv_factor = '0.00'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1124/reference/shift_booker.testclasses.abap b/tasks_gen/train/G1124/reference/shift_booker.testclasses.abap new file mode 100644 index 0000000..d1b5dfa --- /dev/null +++ b/tasks_gen/train/G1124/reference/shift_booker.testclasses.abap @@ -0,0 +1,105 @@ +CLASS ltc_shift_booker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + DATA mo_cut TYPE REF TO {{p}}shift_booker. + + METHODS setup. + METHODS valid_shift + RETURNING VALUE(rs_shift) TYPE {{p}}shift_booker=>ty_shift. + METHODS expect_reason + IMPORTING is_shift TYPE {{p}}shift_booker=>ty_shift + iv_reason TYPE ty_reason. + METHODS expect_pay + IMPORTING is_shift TYPE {{p}}shift_booker=>ty_shift + iv_pay TYPE {{p}}shift_booker=>ty_amount. + + METHODS standard_pay FOR TESTING. + METHODS overtime_pay FOR TESTING. + METHODS empty_order FOR TESTING. + METHODS zero_hours FOR TESTING. + METHODS bad_mode FOR TESTING. + METHODS bad_group FOR TESTING. + METHODS zero_pieces FOR TESTING. +ENDCLASS. + + +CLASS ltc_shift_booker IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}shift_booker( ). + ENDMETHOD. + + METHOD valid_shift. + rs_shift = VALUE #( order_no = '4711' + start_date = '20240212' + work_hours = '8.00' + calc_mode = 'STD' + group_code = 'A' + piece_count = 100 ). + ENDMETHOD. + + METHOD expect_reason. + TRY. + mo_cut->evaluate( is_shift ). + cl_abap_unit_assert=>fail( msg = |no exception for { iv_reason }| ). + CATCH {{p}}cx_shift_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = iv_reason + act = lx_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD expect_pay. + TRY. + cl_abap_unit_assert=>assert_equals( exp = iv_pay + act = mo_cut->evaluate( is_shift ) ). + CATCH {{p}}cx_shift_error. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD standard_pay. + expect_pay( is_shift = valid_shift( ) iv_pay = '194.00' ). + ENDMETHOD. + + METHOD overtime_pay. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '7.50'. + ls_shift-calc_mode = 'OT1'. + ls_shift-group_code = 'B'. + ls_shift-piece_count = 40. + expect_pay( is_shift = ls_shift iv_pay = '267.50' ). + ENDMETHOD. + + METHOD empty_order. + DATA(ls_shift) = valid_shift( ). + ls_shift-order_no = ''. + expect_reason( is_shift = ls_shift iv_reason = 'ORDER' ). + ENDMETHOD. + + METHOD zero_hours. + DATA(ls_shift) = valid_shift( ). + ls_shift-work_hours = '0.00'. + expect_reason( is_shift = ls_shift iv_reason = 'HOURS' ). + ENDMETHOD. + + METHOD bad_mode. + DATA(ls_shift) = valid_shift( ). + ls_shift-calc_mode = 'XX'. + expect_reason( is_shift = ls_shift iv_reason = 'MODE' ). + ENDMETHOD. + + METHOD bad_group. + DATA(ls_shift) = valid_shift( ). + ls_shift-group_code = 'D'. + expect_reason( is_shift = ls_shift iv_reason = 'GROUP' ). + ENDMETHOD. + + METHOD zero_pieces. + DATA(ls_shift) = valid_shift( ). + ls_shift-piece_count = 0. + expect_reason( is_shift = ls_shift iv_reason = 'COUNT' ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1124/spec.md b/tasks_gen/train/G1124/spec.md new file mode 100644 index 0000000..fedf3ce --- /dev/null +++ b/tasks_gen/train/G1124/spec.md @@ -0,0 +1,76 @@ +# 1. Goal +Check a work booking of a production shift and calculate the pay of the shift. A +payroll program calls the check before it posts the pay. The program needs one +reason code for each booking that is not correct. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SHIFT_BOOKER does not exist. You create it. +- The exception class {{P}}CX_SHIFT_ERROR does not exist. You create it. +- The business uses the words hours, mode, order, date, count, group and value. + These words are reserved words or SQL words. The contract below uses other + names for the same concepts. + +# 4. Contract +## 4.1 Exception class {{P}}CX_SHIFT_ERROR +- The class is in package $TMP. It inherits from CX_STATIC_CHECK. +- The class has the public instance attribute MV_REASON. The type is CHAR 10. + MV_REASON holds the reason code of the error. +- Do not add other public components. + +## 4.2 Class {{P}}SHIFT_BOOKER +- The class is in package $TMP. It is public, final, and has a public constructor + without parameters. +- Public types: + - TY_AMOUNT: packed number, length 9, 2 decimal places. + - TY_HOURS: packed number, length 5, 2 decimal places. + - TY_MODE: CHAR 3. + - TY_GROUP: CHAR 1. + - TY_ORDER: CHAR 10. + - TY_SHIFT: structure with these components: + - ORDER_NO: TYPE TY_ORDER + - START_DATE: TYPE d + - WORK_HOURS: TYPE TY_HOURS + - CALC_MODE: TYPE TY_MODE + - GROUP_CODE: TYPE TY_GROUP + - PIECE_COUNT: TYPE i +- Public method: + EVALUATE + IMPORTING is_shift TYPE ty_shift + RETURNING VALUE(rv_pay) TYPE ty_amount + RAISING {{P}}CX_SHIFT_ERROR +- Do not add other public methods. + +# 5. Business rules +The method EVALUATE checks the booking. Apply the rules in this sequence. The +method raises {{P}}CX_SHIFT_ERROR with the reason code of the first rule that +fails. +1. If ORDER_NO is empty, the reason code is ORDER. +2. If START_DATE is initial, the reason code is DATE. +3. If WORK_HOURS is not greater than zero, the reason code is HOURS. +4. CALC_MODE must be STD, OT1 or OT2. If not, the reason code is MODE. +5. GROUP_CODE must be A, B or C. If not, the reason code is GROUP. +6. If PIECE_COUNT is less than 1, the reason code is COUNT. +If all rules pass, the method returns the value of the shift. + +The value of the shift is: + WORK_HOURS x base rate x mode factor + PIECE_COUNT x 0.50 +- The base rate of the group is: A = 18.00, B = 22.00, C = 26.00. +- The mode factor is: STD = 1.0, OT1 = 1.5, OT2 = 2.0. +- The value has 2 decimal places. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Do not use the words HOURS, MODE, ORDER, DATE, COUNT, GROUP or VALUE as the name + of a field, of a component or of a parameter. The reason codes in the rules + above are values, not names. +- Out of scope: do not create other objects. Do not use transports. + +# 7. Acceptance +- The two classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}SHIFT_BOOKER. diff --git a/tasks_gen/train/G1124/task.json b/tasks_gen/train/G1124/task.json new file mode 100644 index 0000000..0a61872 --- /dev/null +++ b/tasks_gen/train/G1124/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1124", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_SHIFT_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}SHIFT_BOOKER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}D02_HIDDEN", + "file": "hidden/d02_hidden.clas.abap", + "description": "D02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_SHIFT_ERROR", + "file": "reference/cx_shift_error.clas.abap", + "description": "Shift error exception class" + }, + { + "type": "CLAS", + "name": "{{P}}SHIFT_BOOKER", + "file": "reference/shift_booker.clas.abap", + "description": "Shift booker", + "testclasses_file": "reference/shift_booker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_reserved_words" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1125/faulty/m0_prt_price.func.abap b/tasks_gen/train/G1125/faulty/m0_prt_price.func.abap new file mode 100644 index 0000000..ae3f4c6 --- /dev/null +++ b/tasks_gen/train/G1125/faulty/m0_prt_price.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}prt_price + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + CONSTANTS: + lc_price_fl TYPE decfloat34 VALUE '0.05', + lc_price_bc TYPE decfloat34 VALUE '0.08', + lc_price_po TYPE decfloat34 VALUE '1.15', + lc_min_fl TYPE decfloat34 VALUE '10', + lc_min_bc TYPE decfloat34 VALUE '5', + lc_discount TYPE decfloat34 VALUE '0.9', + lc_express TYPE decfloat34 VALUE '1.25'. + + ev_skip = abap_true. + ev_note = ''. + cv_price = 0. + + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + + CASE iv_job_type. + WHEN 'FL'. + cv_price = round( val = iv_quantity * lc_price_fl dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_fl. + cv_price = lc_min_fl. + ENDIF. + WHEN 'BC'. + cv_price = round( val = iv_quantity * lc_price_bc dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_bc. + cv_price = lc_min_bc. + ENDIF. + WHEN 'PO'. + cv_price = round( val = iv_quantity * lc_price_po dec = 2 mode = cl_abap_math=>round_half_up ). + IF iv_quantity > 100. + cv_price = round( val = cv_price * lc_discount dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + ev_note = 'MANUAL QUOTE'. + ENDCASE. + + IF iv_express = abap_true AND cv_price > 0. + cv_price = round( val = cv_price * lc_express dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/faulty/m2_prt_price.func.abap b/tasks_gen/train/G1125/faulty/m2_prt_price.func.abap new file mode 100644 index 0000000..5a78ca7 --- /dev/null +++ b/tasks_gen/train/G1125/faulty/m2_prt_price.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}prt_price + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + CONSTANTS: + lc_price_fl TYPE decfloat34 VALUE '0.05', + lc_price_bc TYPE decfloat34 VALUE '0.08', + lc_price_po TYPE decfloat34 VALUE '1.15', + lc_min_fl TYPE decfloat34 VALUE '10', + lc_min_bc TYPE decfloat34 VALUE '5', + lc_discount TYPE decfloat34 VALUE '0.9', + lc_express TYPE decfloat34 VALUE '1.25'. + + ev_skip = abap_false. + ev_note = ''. + cv_price = 0. + + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + + CASE iv_job_type. + WHEN 'FL'. + cv_price = round( val = iv_quantity * lc_price_fl dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_fl. + cv_price = lc_min_fl. + ENDIF. + WHEN 'BC'. + cv_price = round( val = iv_quantity * lc_price_bc dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_bc. + cv_price = lc_min_bc. + ENDIF. + WHEN 'PO'. + cv_price = round( val = iv_quantity * lc_price_po dec = 2 mode = cl_abap_math=>round_half_up ). + IF iv_quantity <= 100. + cv_price = round( val = cv_price * lc_discount dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + ev_note = 'MANUAL QUOTE'. + ENDCASE. + + IF iv_express = abap_true AND cv_price > 0. + cv_price = round( val = cv_price * lc_express dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/faulty/m3_prt_price.func.abap b/tasks_gen/train/G1125/faulty/m3_prt_price.func.abap new file mode 100644 index 0000000..f3f4bb5 --- /dev/null +++ b/tasks_gen/train/G1125/faulty/m3_prt_price.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}prt_price + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + CONSTANTS: + lc_price_fl TYPE decfloat34 VALUE '0.05', + lc_price_bc TYPE decfloat34 VALUE '0.08', + lc_price_po TYPE decfloat34 VALUE '1.15', + lc_min_fl TYPE decfloat34 VALUE '10', + lc_min_bc TYPE decfloat34 VALUE '5', + lc_discount TYPE decfloat34 VALUE '0.9', + lc_express TYPE decfloat34 VALUE '1.25'. + + ev_skip = abap_false. + ev_note = ''. + cv_price = 0. + + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + + CASE iv_job_type. + WHEN 'FL'. + cv_price = round( val = iv_quantity * lc_price_fl dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_fl. + cv_price = lc_min_fl. + ENDIF. + WHEN 'BC'. + cv_price = round( val = iv_quantity * lc_price_bc dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_bc. + cv_price = lc_min_bc. + ENDIF. + WHEN 'PO'. + cv_price = round( val = iv_quantity * lc_price_po dec = 2 mode = cl_abap_math=>round_half_up ). + IF iv_quantity > 100. + cv_price = round( val = cv_price * lc_discount dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + ev_note = 'MANUAL QUOTE'. + ENDCASE. + + IF iv_express <> abap_true AND cv_price > 0. + cv_price = round( val = cv_price * lc_express dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/faulty/m4_prt_price.func.abap b/tasks_gen/train/G1125/faulty/m4_prt_price.func.abap new file mode 100644 index 0000000..a9949bd --- /dev/null +++ b/tasks_gen/train/G1125/faulty/m4_prt_price.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}prt_price + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + CONSTANTS: + lc_price_fl TYPE decfloat34 VALUE '0.05', + lc_price_bc TYPE decfloat34 VALUE '0.08', + lc_price_po TYPE decfloat34 VALUE '1.15', + lc_min_fl TYPE decfloat34 VALUE '10', + lc_min_bc TYPE decfloat34 VALUE '5', + lc_discount TYPE decfloat34 VALUE '0.9', + lc_express TYPE decfloat34 VALUE '1.25'. + + ev_skip = abap_false. + ev_note = ''. + cv_price = 0. + + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + + CASE iv_job_type. + WHEN 'FL'. + cv_price = round( val = iv_quantity * lc_price_fl dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_fl. + cv_price = lc_min_fl. + ENDIF. + WHEN 'BC'. + cv_price = round( val = iv_quantity * lc_price_bc dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_bc. + cv_price = lc_min_bc. + ENDIF. + WHEN 'PO'. + cv_price = round( val = iv_quantity * lc_price_po dec = 2 mode = cl_abap_math=>round_half_up ). + IF iv_quantity > 100. + cv_price = round( val = cv_price * lc_discount dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + ev_note = 'MANUAL QUOTE'. + ENDCASE. + + IF iv_express = abap_true AND cv_price > 0. + cv_price = round( val = cv_price / lc_express dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/generation.json b/tasks_gen/train/G1125/generation.json new file mode 100644 index 0000000..4564cbb --- /dev/null +++ b/tasks_gen/train/G1125/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1125", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1125/hidden/prt_hidden.clas.abap b/tasks_gen/train/G1125/hidden/prt_hidden.clas.abap new file mode 100644 index 0000000..8727c20 --- /dev/null +++ b/tasks_gen/train/G1125/hidden/prt_hidden.clas.abap @@ -0,0 +1,121 @@ +CLASS {{p}}prt_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool DEFAULT abap_false + iv_price TYPE decfloat34 DEFAULT 0 + EXPORTING ev_skip TYPE abap_bool + ev_note TYPE string + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS flyers_price FOR TESTING. + METHODS flyers_minimum FOR TESTING. + METHODS cards_price FOR TESTING. + METHODS cards_minimum FOR TESTING. + METHODS posters_discount FOR TESTING. + METHODS posters_no_discount FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS manual_quote FOR TESTING. + METHODS invalid_quantity FOR TESTING. + METHODS skip_false_for_flyers FOR TESTING. +ENDCLASS. + + +CLASS {{p}}prt_hidden IMPLEMENTATION. + METHOD call. + ev_price = iv_price. + ev_note = ''. + CALL FUNCTION '{{P}}PRT_PRICE' + EXPORTING + iv_job_type = iv_type + iv_quantity = iv_quantity + iv_express = iv_express + IMPORTING + ev_skip = ev_skip + ev_note = ev_note + CHANGING + cv_price = ev_price + EXCEPTIONS + invalid_quantity = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD flyers_price. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `FL` iv_quantity = 400 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) act = lv_price ). + ENDMETHOD. + + METHOD flyers_minimum. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `FL` iv_quantity = 10 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) act = lv_price ). + ENDMETHOD. + + METHOD cards_price. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `BC` iv_quantity = 100 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '8.00' ) act = lv_price ). + ENDMETHOD. + + METHOD cards_minimum. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `BC` iv_quantity = 10 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.00' ) act = lv_price ). + ENDMETHOD. + + METHOD posters_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `PO` iv_quantity = 101 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '104.54' ) act = lv_price ). + ENDMETHOD. + + METHOD posters_no_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `PO` iv_quantity = 100 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '115.00' ) act = lv_price ). + ENDMETHOD. + + METHOD express_surcharge. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `FL` iv_quantity = 202 iv_express = abap_true + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.63' ) act = lv_price ). + ENDMETHOD. + + METHOD manual_quote. + DATA lv_skip TYPE abap_bool. + DATA lv_note TYPE string. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_type = `XX` iv_quantity = 50 + IMPORTING ev_skip = lv_skip ev_note = lv_note ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = `MANUAL QUOTE` act = lv_note ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.00' ) act = lv_price ). + ENDMETHOD. + + METHOD invalid_quantity. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_type = `FL` iv_quantity = 0 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD skip_false_for_flyers. + DATA lv_skip TYPE abap_bool. + DATA lv_note TYPE string. + call( EXPORTING iv_type = `FL` iv_quantity = 400 + IMPORTING ev_skip = lv_skip ev_note = lv_note ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + cl_abap_unit_assert=>assert_initial( lv_note ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1125/mutation.json b/tasks_gen/train/G1125/mutation.json new file mode 100644 index 0000000..650f64f --- /dev/null +++ b/tasks_gen/train/G1125/mutation.json @@ -0,0 +1,59 @@ +{ + "task": "G1125", + "mutants": [ + { + "object": "{{P}}PRT_PRICE", + "mutant": "line 23: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "SKIP_FALSE_FOR_FLYERS" + ] + }, + { + "object": "{{P}}PRT_PRICE", + "mutant": "line 43: 2 -> 3 (const)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + }, + { + "object": "{{P}}PRT_PRICE", + "mutant": "line 44: > -> <= (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "POSTERS_DISCOUNT", + "POSTERS_NO_DISCOUNT" + ] + }, + { + "object": "{{P}}PRT_PRICE", + "mutant": "line 52: = -> <> (eq)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "CARDS_MINIMUM", + "CARDS_PRICE", + "EXPRESS_SURCHARGE", + "FLYERS_MINIMUM", + "FLYERS_PRICE", + "POSTERS_DISCOUNT", + "POSTERS_NO_DISCOUNT" + ] + }, + { + "object": "{{P}}PRT_PRICE", + "mutant": "line 53: * -> / (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "EXPRESS_SURCHARGE" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1125/reference/prt_price.func.abap b/tasks_gen/train/G1125/reference/prt_price.func.abap new file mode 100644 index 0000000..c89e78b --- /dev/null +++ b/tasks_gen/train/G1125/reference/prt_price.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}prt_price + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + CONSTANTS: + lc_price_fl TYPE decfloat34 VALUE '0.05', + lc_price_bc TYPE decfloat34 VALUE '0.08', + lc_price_po TYPE decfloat34 VALUE '1.15', + lc_min_fl TYPE decfloat34 VALUE '10', + lc_min_bc TYPE decfloat34 VALUE '5', + lc_discount TYPE decfloat34 VALUE '0.9', + lc_express TYPE decfloat34 VALUE '1.25'. + + ev_skip = abap_false. + ev_note = ''. + cv_price = 0. + + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + + CASE iv_job_type. + WHEN 'FL'. + cv_price = round( val = iv_quantity * lc_price_fl dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_fl. + cv_price = lc_min_fl. + ENDIF. + WHEN 'BC'. + cv_price = round( val = iv_quantity * lc_price_bc dec = 2 mode = cl_abap_math=>round_half_up ). + IF cv_price < lc_min_bc. + cv_price = lc_min_bc. + ENDIF. + WHEN 'PO'. + cv_price = round( val = iv_quantity * lc_price_po dec = 2 mode = cl_abap_math=>round_half_up ). + IF iv_quantity > 100. + cv_price = round( val = cv_price * lc_discount dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + WHEN OTHERS. + ev_skip = abap_true. + ev_note = 'MANUAL QUOTE'. + ENDCASE. + + IF iv_express = abap_true AND cv_price > 0. + cv_price = round( val = cv_price * lc_express dec = 2 mode = cl_abap_math=>round_half_up ). + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/reference/prt_test.clas.abap b/tasks_gen/train/G1125/reference/prt_test.clas.abap new file mode 100644 index 0000000..015a6f1 --- /dev/null +++ b/tasks_gen/train/G1125/reference/prt_test.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}prt_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_of + IMPORTING iv_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool DEFAULT abap_false + EXPORTING ev_skip TYPE abap_bool + ev_note TYPE string + ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS flyers_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS posters_fee FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS manual_quote FOR TESTING. + METHODS invalid_quantity FOR TESTING. +ENDCLASS. + + +CLASS {{p}}prt_test IMPLEMENTATION. + METHOD price_of. + ev_skip = abap_false. + ev_note = ''. + ev_price = 0. + CALL FUNCTION '{{P}}PRT_PRICE' + EXPORTING + iv_job_type = iv_type + iv_quantity = iv_quantity + iv_express = iv_express + IMPORTING + ev_skip = ev_skip + ev_note = ev_note + CHANGING + cv_price = ev_price + EXCEPTIONS + invalid_quantity = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD flyers_fee. + DATA lv_price TYPE decfloat34. + price_of( EXPORTING iv_type = `FL` iv_quantity = 400 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '20.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_price TYPE decfloat34. + price_of( EXPORTING iv_type = `FL` iv_quantity = 10 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '10.00' ) + act = lv_price ). + price_of( EXPORTING iv_type = `BC` iv_quantity = 10 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '5.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD posters_fee. + DATA lv_price TYPE decfloat34. + price_of( EXPORTING iv_type = `PO` iv_quantity = 100 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '115.00' ) + act = lv_price ). + price_of( EXPORTING iv_type = `PO` iv_quantity = 101 + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '104.54' ) + act = lv_price ). + ENDMETHOD. + + METHOD express_fee. + DATA lv_price TYPE decfloat34. + price_of( EXPORTING iv_type = `FL` iv_quantity = 202 iv_express = abap_true + IMPORTING ev_price = lv_price ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '12.63' ) + act = lv_price ). + ENDMETHOD. + + METHOD manual_quote. + DATA lv_skip TYPE abap_bool. + DATA lv_note TYPE string. + DATA lv_price TYPE decfloat34. + price_of( EXPORTING iv_type = `XX` iv_quantity = 50 + IMPORTING ev_skip = lv_skip ev_note = lv_note ev_price = lv_price ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = `MANUAL QUOTE` act = lv_note ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '0.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD invalid_quantity. + DATA lv_subrc TYPE sysubrc. + price_of( EXPORTING iv_type = `FL` iv_quantity = 0 + IMPORTING ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1125/seed/prt_sample.func.abap b/tasks_gen/train/G1125/seed/prt_sample.func.abap new file mode 100644 index 0000000..1388249 --- /dev/null +++ b/tasks_gen/train/G1125/seed/prt_sample.func.abap @@ -0,0 +1,18 @@ +FUNCTION {{p}}prt_sample + IMPORTING + iv_job_type TYPE string + iv_quantity TYPE i + iv_express TYPE abap_bool + EXPORTING + ev_skip TYPE abap_bool + ev_note TYPE string + CHANGING + cv_price TYPE decfloat34 + EXCEPTIONS + invalid_quantity. + + ev_skip = abap_false. + ev_note = ''. + cv_price = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1125/spec.md b/tasks_gen/train/G1125/spec.md new file mode 100644 index 0000000..2dc6f66 --- /dev/null +++ b/tasks_gen/train/G1125/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +A print shop prices print jobs. The order desk calls one pricing module for each +print job. The pricing module must have the same interface as the sample pricing +module, because the order desk calls all pricing modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_PRT exists in package $TMP. +- Function module {{P}}PRT_SAMPLE exists in this group. It is the sample pricing + module. Its interface is the interface that all pricing modules must use. + +# 4. Contract +- Create the function module {{P}}PRT_PRICE in function group {{P}}FG_PRT. +- Use exactly the interface of {{P}}PRT_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the quantity is less than 1, raise the exception INVALID_QUANTITY. +2. Set the skip flag to false and the note to an empty string, except in rule 7. +3. Job type FL (flyers): the price is the quantity multiplied by 0.05, rounded + half up to 2 decimals. If the price is less than 10.00, the price is 10.00. +4. Job type BC (business cards): the price is the quantity multiplied by 0.08, + rounded half up to 2 decimals. If the price is less than 5.00, the price is + 5.00. +5. Job type PO (posters): the price is the quantity multiplied by 1.15, rounded + half up to 2 decimals. If the quantity is greater than 100, the price is + reduced by 10 %. The reduced price is rounded half up to 2 decimals. +6. Express: if the express flag is true and the price is greater than 0, the + price is increased by 25 %. The increased price is rounded half up to 2 + decimals. +7. Any other job type: the price is 0, the skip flag is true, and the note is + MANUAL QUOTE. Rule 6 does not apply to these job types. +8. The changing parameter always contains the final price. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}PRT_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1125/task.json b/tasks_gen/train/G1125/task.json new file mode 100644 index 0000000..04a7dce --- /dev/null +++ b/tasks_gen/train/G1125/task.json @@ -0,0 +1,88 @@ +{ + "id": "G1125", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_PRT", + "description": "Print job pricing" + }, + { + "type": "FUNC", + "name": "{{P}}PRT_SAMPLE", + "functionGroup": "{{P}}FG_PRT", + "file": "seed/prt_sample.func.abap", + "description": "Sample pricing module that defines the interface" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}PRT_PRICE", + "functionGroup": "{{P}}FG_PRT", + "params": [ + { + "name": "iv_job_type", + "type": "string" + }, + { + "name": "iv_quantity", + "type": "i" + }, + { + "name": "iv_express", + "type": "abap_bool" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "ev_note", + "type": "string" + }, + { + "name": "cv_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}PRT_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}PRT_HIDDEN", + "file": "hidden/prt_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}PRT_PRICE", + "functionGroup": "{{P}}FG_PRT", + "file": "reference/prt_price.func.abap", + "description": "Print job pricing module" + }, + { + "type": "CLAS", + "name": "{{P}}PRT_TEST", + "file": "reference/prt_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1126/generation.json b/tasks_gen/train/G1126/generation.json new file mode 100644 index 0000000..4565ba6 --- /dev/null +++ b/tasks_gen/train/G1126/generation.json @@ -0,0 +1,37 @@ +{ + "id": "G1126", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/vet_seed.clas.abap line 9: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"INSERT\"", + "hidden/vet_hidden.clas.abap line 48: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"ASSIGN\"", + "hidden/vet_hidden.clas.abap line 49: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"ASSIGN\"", + "hidden/vet_hidden.clas.abap line 51: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"rv_value\"", + "hidden/vet_hidden.clas.abap line 79: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"" + ], + "by_check": { + "abaplint": 5 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 80.5, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1126/hidden/vet_hidden.clas.abap b/tasks_gen/train/G1126/hidden/vet_hidden.clas.abap new file mode 100644 index 0000000..4a76542 --- /dev/null +++ b/tasks_gen/train/G1126/hidden/vet_hidden.clas.abap @@ -0,0 +1,135 @@ +CLASS {{p}}vet_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_anim TYPE RANGE OF {{p}}treat-animal. + METHODS run + IMPORTING iv_from TYPE d + it_anim TYPE tt_anim OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS all_animals FOR TESTING. + METHODS sorted_by_total FOR TESTING. + METHODS totals_and_counts FOR TESTING. + METHODS frequent_flag FOR TESTING. + METHODS animal_filter FOR TESTING. + METHODS date_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}vet_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}vet_sum WITH p_from = iv_from WITH s_anim IN it_anim AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + FIELD-SYMBOLS TYPE any. + FIELD-SYMBOLS TYPE any. + DATA lv_str TYPE string. + + ASSIGN ir_data->* TO . + READ TABLE INDEX iv_row ASSIGNING . + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Row missing' ). + ASSIGN COMPONENT iv_col OF STRUCTURE TO . + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Column missing' ). + lv_str = . + CONDENSE lv_str. + rv_value = lv_str. + ENDMETHOD. + + METHOD all_animals. + DATA lr_data TYPE REF TO data. + DATA lv_lines TYPE i. + + lr_data = run( '20260101' ). + lv_lines = lines_of( lr_data ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_lines ). + ENDMETHOD. + + METHOD sorted_by_total. + DATA lr_data TYPE REF TO data. + DATA lv_value TYPE string. + + lr_data = run( '20260101' ). + lv_value = value_of( ir_data = lr_data iv_row = 1 iv_col = 'ANIMAL' ). + cl_abap_unit_assert=>assert_equals( exp = 'CAT' act = lv_value ). + lv_value = value_of( ir_data = lr_data iv_row = 3 iv_col = 'ANIMAL' ). + cl_abap_unit_assert=>assert_equals( exp = 'BIRD' act = lv_value ). + ENDMETHOD. + + METHOD totals_and_counts. + DATA lr_data TYPE REF TO data. + DATA lv_value TYPE string. + + lr_data = run( '20260101' ). + lv_value = value_of( ir_data = lr_data iv_row = 1 iv_col = 'TOTAL' ). + cl_abap_unit_assert=>assert_equals( exp = '340.00' act = lv_value ). + lv_value = value_of( ir_data = lr_data iv_row = 1 iv_col = 'CNT' ). + cl_abap_unit_assert=>assert_equals( exp = '2' act = lv_value ). + lv_value = value_of( ir_data = lr_data iv_row = 2 iv_col = 'TOTAL' ). + cl_abap_unit_assert=>assert_equals( exp = '250.00' act = lv_value ). + lv_value = value_of( ir_data = lr_data iv_row = 2 iv_col = 'CNT' ). + cl_abap_unit_assert=>assert_equals( exp = '3' act = lv_value ). + ENDMETHOD. + + METHOD frequent_flag. + DATA lr_data TYPE REF TO data. + DATA lv_value TYPE string. + + lr_data = run( '20260101' ). + lv_value = value_of( ir_data = lr_data iv_row = 2 iv_col = 'FREQUENT' ). + cl_abap_unit_assert=>assert_equals( exp = 'X' act = lv_value ). + lv_value = value_of( ir_data = lr_data iv_row = 1 iv_col = 'FREQUENT' ). + cl_abap_unit_assert=>assert_equals( exp = '' act = lv_value ). + ENDMETHOD. + + METHOD animal_filter. + DATA lr_data TYPE REF TO data. + DATA lv_lines TYPE i. + DATA lv_value TYPE string. + DATA lt_anim TYPE tt_anim. + DATA ls_anim LIKE LINE OF lt_anim. + + CLEAR ls_anim. + ls_anim-sign = 'I'. + ls_anim-option = 'EQ'. + ls_anim-low = 'DOG'. + APPEND ls_anim TO lt_anim. + + lr_data = run( iv_from = '20260101' it_anim = lt_anim ). + lv_lines = lines_of( lr_data ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_lines ). + lv_value = value_of( ir_data = lr_data iv_row = 1 iv_col = 'TOTAL' ). + cl_abap_unit_assert=>assert_equals( exp = '250.00' act = lv_value ). + ENDMETHOD. + + METHOD date_filter. + DATA lr_data TYPE REF TO data. + DATA lv_lines TYPE i. + + lr_data = run( '20260210' ). + lv_lines = lines_of( lr_data ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_lines ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1126/mutation.json b/tasks_gen/train/G1126/mutation.json new file mode 100644 index 0000000..8d8ffa3 --- /dev/null +++ b/tasks_gen/train/G1126/mutation.json @@ -0,0 +1,42 @@ +{ + "task": "T15", + "mutants": [ + { + "object": "{{P}}VET_SUM", + "mutant": "line 43: >= -> < (rel)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_ANIMALS", + "SORTED_BY_TOTAL", + "TOTALS_AND_COUNTS", + "FREQUENT_FLAG", + "ANIMAL_FILTER", + "DATE_FILTER" + ] + }, + { + "object": "{{P}}VET_SUM", + "mutant": "line 68: >= -> > (rel)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "FREQUENT_FLAG" + ] + }, + { + "object": "{{P}}VET_SUM", + "mutant": "line 73: DESCENDING -> ASCENDING (sort)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "SORTED_BY_TOTAL", + "TOTALS_AND_COUNTS" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1126/reference/vet_sum.prog.abap b/tasks_gen/train/G1126/reference/vet_sum.prog.abap new file mode 100644 index 0000000..768bdbd --- /dev/null +++ b/tasks_gen/train/G1126/reference/vet_sum.prog.abap @@ -0,0 +1,203 @@ +REPORT {{p}}vet_sum. + +TYPES: BEGIN OF ty_treat, + treat_date TYPE {{p}}treat-treat_date, + animal TYPE {{p}}treat-animal, + fee TYPE {{p}}treat-fee, + END OF ty_treat. +TYPES tt_treat TYPE STANDARD TABLE OF ty_treat. +TYPES: BEGIN OF ty_line, + animal TYPE {{p}}treat-animal, + total TYPE {{p}}treat-fee, + cnt TYPE i, + frequent TYPE c LENGTH 1, + END OF ty_line. +TYPES tt_line TYPE STANDARD TABLE OF ty_line. +TYPES tt_anim TYPE RANGE OF {{p}}treat-animal. + +DATA gv_animal TYPE {{p}}treat-animal. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_anim FOR gv_animal. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + METHODS read_treatments + IMPORTING iv_from TYPE d + it_anim TYPE tt_anim + EXPORTING et_treat TYPE tt_treat. + METHODS build_lines + IMPORTING it_treat TYPE tt_treat + EXPORTING et_lines TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_treatments. + SELECT treat_date animal fee + FROM {{p}}treat + INTO TABLE et_treat + WHERE treat_date >= iv_from + AND animal IN it_anim. + ENDMETHOD. + + METHOD build_lines. + DATA ls_treat TYPE ty_treat. + DATA ls_line TYPE ty_line. + FIELD-SYMBOLS TYPE ty_line. + + CLEAR et_lines. + LOOP AT it_treat INTO ls_treat. + READ TABLE et_lines ASSIGNING WITH KEY animal = ls_treat-animal. + IF sy-subrc = 0. + -total = -total + ls_treat-fee. + -cnt = -cnt + 1. + ELSE. + CLEAR ls_line. + ls_line-animal = ls_treat-animal. + ls_line-total = ls_treat-fee. + ls_line-cnt = 1. + INSERT ls_line INTO TABLE et_lines. + ENDIF. + ENDLOOP. + + LOOP AT et_lines ASSIGNING . + IF -cnt >= 3. + -frequent = 'X'. + ENDIF. + ENDLOOP. + + SORT et_lines BY total DESCENDING. + ENDMETHOD. + + METHOD display. + DATA lo_alv TYPE REF TO cl_salv_table. + DATA lx_msg TYPE REF TO cx_salv_msg. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = lo_alv + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO lx_msg. + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +DATA: go_report TYPE REF TO lcl_report, + gt_treat TYPE tt_treat, + gt_lines TYPE tt_line. + +START-OF-SELECTION. + CREATE OBJECT go_report. + go_report->read_treatments( EXPORTING iv_from = p_from + it_anim = s_anim[] + IMPORTING et_treat = gt_treat ). + go_report->build_lines( EXPORTING it_treat = gt_treat + IMPORTING et_lines = gt_lines ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mt_treat TYPE tt_treat. + METHODS add_treatment + IMPORTING iv_date TYPE d + iv_animal TYPE {{p}}treat-animal + iv_fee TYPE {{p}}treat-fee. + METHODS aggregate + EXPORTING et_lines TYPE tt_line. + METHODS totals_per_animal FOR TESTING. + METHODS frequent_flag FOR TESTING. + METHODS sorted_by_total FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD add_treatment. + DATA ls_treat TYPE ty_treat. + ls_treat-treat_date = iv_date. + ls_treat-animal = iv_animal. + ls_treat-fee = iv_fee. + APPEND ls_treat TO mt_treat. + ENDMETHOD. + + METHOD aggregate. + DATA lo_report TYPE REF TO lcl_report. + CREATE OBJECT lo_report. + lo_report->build_lines( EXPORTING it_treat = mt_treat + IMPORTING et_lines = et_lines ). + ENDMETHOD. + + METHOD totals_per_animal. + DATA lt_lines TYPE tt_line. + DATA ls_line TYPE ty_line. + DATA lv_expected TYPE {{p}}treat-fee. + DATA lv_animal TYPE {{p}}treat-animal. + + add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). + add_treatment( iv_date = '20260102' iv_animal = 'DOG' iv_fee = '5.00' ). + add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '40.00' ). + aggregate( IMPORTING et_lines = lt_lines ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + + READ TABLE lt_lines INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + lv_animal = 'CAT'. + cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_line-cnt ). + lv_expected = '40.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_line-total ). + + READ TABLE lt_lines INTO ls_line INDEX 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + lv_animal = 'DOG'. + cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-cnt ). + lv_expected = '15.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_line-total ). + ENDMETHOD. + + METHOD frequent_flag. + DATA lt_lines TYPE tt_line. + DATA ls_line TYPE ty_line. + DATA lv_animal TYPE {{p}}treat-animal. + + add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). + add_treatment( iv_date = '20260102' iv_animal = 'DOG' iv_fee = '10.00' ). + add_treatment( iv_date = '20260103' iv_animal = 'DOG' iv_fee = '10.00' ). + add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '10.00' ). + aggregate( IMPORTING et_lines = lt_lines ). + + lv_animal = 'DOG'. + READ TABLE lt_lines INTO ls_line WITH KEY animal = lv_animal. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 'X' act = ls_line-frequent ). + + lv_animal = 'CAT'. + READ TABLE lt_lines INTO ls_line WITH KEY animal = lv_animal. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_initial( act = ls_line-frequent ). + ENDMETHOD. + + METHOD sorted_by_total. + DATA lt_lines TYPE tt_line. + DATA ls_line TYPE ty_line. + DATA lv_animal TYPE {{p}}treat-animal. + + add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). + add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '40.00' ). + add_treatment( iv_date = '20260101' iv_animal = 'BIRD' iv_fee = '25.00' ). + aggregate( IMPORTING et_lines = lt_lines ). + + READ TABLE lt_lines INTO ls_line INDEX 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + lv_animal = 'CAT'. + cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). + + READ TABLE lt_lines INTO ls_line INDEX 3. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + lv_animal = 'BIRD'. + cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1126/seed/treat.tabl.asabap b/tasks_gen/train/G1126/seed/treat.tabl.asabap new file mode 100644 index 0000000..63fa15b --- /dev/null +++ b/tasks_gen/train/G1126/seed/treat.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Veterinary treatments' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}treat { + key client : abap.clnt not null; + key treat_id : abap.int4 not null; + treat_date : abap.dats; + animal : abap.char(10); + fee : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1126/seed/vet_seed.clas.abap b/tasks_gen/train/G1126/seed/vet_seed.clas.abap new file mode 100644 index 0000000..d79631f --- /dev/null +++ b/tasks_gen/train/G1126/seed/vet_seed.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}vet_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}vet_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_treat TYPE STANDARD TABLE OF {{p}}treat. + DATA ls_treat TYPE {{p}}treat. + + DELETE FROM {{p}}treat. + + CLEAR ls_treat. + ls_treat-treat_id = 1. + ls_treat-treat_date = '20260105'. + ls_treat-animal = 'DOG'. + ls_treat-fee = '120.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 2. + ls_treat-treat_date = '20260120'. + ls_treat-animal = 'DOG'. + ls_treat-fee = '80.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 3. + ls_treat-treat_date = '20260201'. + ls_treat-animal = 'DOG'. + ls_treat-fee = '50.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 4. + ls_treat-treat_date = '20260110'. + ls_treat-animal = 'CAT'. + ls_treat-fee = '300.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 5. + ls_treat-treat_date = '20260215'. + ls_treat-animal = 'CAT'. + ls_treat-fee = '40.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 6. + ls_treat-treat_date = '20251220'. + ls_treat-animal = 'BIRD'. + ls_treat-fee = '500.00'. + APPEND ls_treat TO lt_treat. + + CLEAR ls_treat. + ls_treat-treat_id = 7. + ls_treat-treat_date = '20260301'. + ls_treat-animal = 'BIRD'. + ls_treat-fee = '25.00'. + APPEND ls_treat TO lt_treat. + + INSERT {{p}}treat FROM TABLE lt_treat. + + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1126/spec.md b/tasks_gen/train/G1126/spec.md new file mode 100644 index 0000000..62a9e49 --- /dev/null +++ b/tasks_gen/train/G1126/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +Show the treatment fee of each animal in an ALV list. The practice manager uses +the list to compare the animals for a period. + +# 2. Open questions +None. + +# 3. Context +- The table {{P}}TREAT exists in package $TMP. It contains data. + Fields: TREAT_ID (INT4, key), TREAT_DATE (DATS), ANIMAL (CHAR 10), + FEE (DEC 15,2). +- The practice writes one row for each treatment. + +# 4. Contract +- Create the executable program {{P}}VET_SUM in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first treatment date to include. + - Select-option S_ANIM for the animal. +- Show the result with CL_SALV_TABLE. The output table has these columns with + these names: + - ANIMAL: the animal. + - TOTAL: the sum of the fees. + - CNT: the number of the treatments. + - FREQUENT: 'X' if CNT is 3 or more, otherwise empty. + +# 5. Business rules +1. Include a treatment only if TREAT_DATE is on or after P_FROM and ANIMAL is + in S_ANIM. +2. Show one line for each animal that has at least one included treatment. +3. TOTAL is the sum of the FEE of the included treatments of the animal. +4. CNT is the number of the included treatments of the animal. +5. FREQUENT is 'X' if CNT is 3 or more. Otherwise FREQUENT is empty. +6. Sort the lines by TOTAL, the highest total first. + +# 6. Constraints +- Release target: 7.02. Use only ABAP syntax that release 7.02 supports. Do not + use inline declarations, constructor expressions, string templates, table + expressions, the NEW operator, or the character '@' in ABAP SQL. +- Coding standards: Clean ABAP. Read the table with one database access. +- Out of scope: do not change the table {{P}}TREAT. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1126/task.json b/tasks_gen/train/G1126/task.json new file mode 100644 index 0000000..28f1857 --- /dev/null +++ b/tasks_gen/train/G1126/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1126", + "category": "G", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v702", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}TREAT", + "file": "seed/treat.tabl.asabap", + "description": "Veterinary treatments" + }, + { + "type": "CLAS", + "name": "{{P}}VET_SEED", + "file": "seed/vet_seed.clas.abap", + "description": "Seed data for {{P}}TREAT", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}VET_SUM", + "parameters": [ + "p_from", + "s_anim" + ] + } + ], + "out_of_scope": [ + "{{P}}TREAT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}VET_HIDDEN", + "file": "hidden/vet_hidden.clas.abap", + "description": "Hidden tests for {{P}}VET_SUM" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}VET_SUM", + "file": "reference/vet_sum.prog.abap", + "description": "Treatment fee per animal" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1127/generation.json b/tasks_gen/train/G1127/generation.json new file mode 100644 index 0000000..3e0db3e --- /dev/null +++ b/tasks_gen/train/G1127/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1127", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1127/reference/key_price.func.abap b/tasks_gen/train/G1127/reference/key_price.func.abap new file mode 100644 index 0000000..fe6a3ec --- /dev/null +++ b/tasks_gen/train/G1127/reference/key_price.func.abap @@ -0,0 +1,34 @@ +FUNCTION {{p}}key_price + IMPORTING + iv_key_type TYPE string + iv_quantity TYPE i + EXPORTING + ev_price TYPE decfloat34 + ev_valid TYPE abap_bool + EXCEPTIONS + invalid_quantity. + + DATA lv_unit_price TYPE decfloat34. + ev_price = 0. + ev_valid = abap_false. + IF iv_quantity < 1. + RAISE invalid_quantity. + ENDIF. + CASE iv_key_type. + WHEN 'ST'. + lv_unit_price = '3.50'. + WHEN 'SE'. + lv_unit_price = '12.00'. + WHEN 'TR'. + lv_unit_price = '45.00'. + WHEN OTHERS. + RETURN. + ENDCASE. + ev_price = lv_unit_price * iv_quantity. + IF iv_quantity >= 10. + ev_price = ev_price * '0.9'. + ENDIF. + ev_price = round( val = ev_price dec = 2 mode = cl_abap_math=>round_half_up ). + ev_valid = abap_true. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1127/reference/key_test.clas.abap b/tasks_gen/train/G1127/reference/key_test.clas.abap new file mode 100644 index 0000000..ef443bf --- /dev/null +++ b/tasks_gen/train/G1127/reference/key_test.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}key_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS price_for IMPORTING iv_key_type TYPE string iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE decfloat34. + METHODS standard_price FOR TESTING. + METHODS security_price FOR TESTING. + METHODS transponder_price FOR TESTING. + METHODS discount FOR TESTING. + METHODS invalid_quantity FOR TESTING. +ENDCLASS. + + +CLASS {{p}}key_test IMPLEMENTATION. + METHOD price_for. + DATA lv_valid TYPE abap_bool. + CALL FUNCTION '{{P}}KEY_PRICE' + EXPORTING iv_key_type = iv_key_type iv_quantity = iv_quantity + IMPORTING ev_price = rv_price ev_valid = lv_valid + EXCEPTIONS invalid_quantity = 1 OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD standard_price. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.50' ) act = price_for( iv_key_type = 'ST' iv_quantity = 3 ) ). + ENDMETHOD. + + METHOD security_price. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '24.00' ) act = price_for( iv_key_type = 'SE' iv_quantity = 2 ) ). + ENDMETHOD. + + METHOD transponder_price. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.00' ) act = price_for( iv_key_type = 'TR' iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD discount. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '31.50' ) act = price_for( iv_key_type = 'ST' iv_quantity = 10 ) ). + ENDMETHOD. + + METHOD invalid_quantity. + DATA lv_price TYPE decfloat34. + DATA lv_valid TYPE abap_bool. + CALL FUNCTION '{{P}}KEY_PRICE' + EXPORTING iv_key_type = 'ST' iv_quantity = 0 + IMPORTING ev_price = lv_price ev_valid = lv_valid + EXCEPTIONS invalid_quantity = 1 OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1127/seed/key_sample.func.abap b/tasks_gen/train/G1127/seed/key_sample.func.abap new file mode 100644 index 0000000..a9da123 --- /dev/null +++ b/tasks_gen/train/G1127/seed/key_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}key_sample + IMPORTING + iv_key_type TYPE string + iv_quantity TYPE i + EXPORTING + ev_price TYPE decfloat34 + ev_valid TYPE abap_bool + EXCEPTIONS + invalid_quantity. + + ev_price = 0. + ev_valid = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1127/spec.md b/tasks_gen/train/G1127/spec.md new file mode 100644 index 0000000..6eefee9 --- /dev/null +++ b/tasks_gen/train/G1127/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +Calculate the price of a key cutting order. The checkout program calls one +function module for each order. The function module must have the same interface +as the sample function module, because the checkout program calls all order +modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_KEY exists in package $TMP. +- Function module {{P}}KEY_SAMPLE exists in this group. It is the sample order + module. Its interface is the interface that all order modules must use. + +# 4. Contract +- Create the function module {{P}}KEY_PRICE in function group {{P}}FG_KEY. +- Use exactly the interface of {{P}}KEY_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the quantity is less than 1, raise the exception INVALID_QUANTITY. +2. Key type ST (standard): the unit price is 3.50. +3. Key type SE (security): the unit price is 12.00. +4. Key type TR (transponder): the unit price is 45.00. +5. The total price is the unit price multiplied by the quantity. +6. If the quantity is 10 or more, the customer gets a 10 % discount on the total + price. +7. Round the total price to 2 decimals, half up. +8. Set ev_valid to true when the order is priced. Set ev_valid to false when the + order is not priced. +9. The shop offers only the key types ST, SE, and TR. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Out of scope: do not change {{P}}KEY_SAMPLE. +- Coding standards: Clean ABAP. + +# 7. Acceptance +- The function module is active and has no syntax error. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1127/task.json b/tasks_gen/train/G1127/task.json new file mode 100644 index 0000000..1dfb8cd --- /dev/null +++ b/tasks_gen/train/G1127/task.json @@ -0,0 +1,92 @@ +{ + "id": "G1127", + "category": "H", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The spec does not define the result for a key type that is not ST, SE, or TR.", + "keywords": [ + [ + "unknown", + "key", + "type" + ], + [ + "other", + "key", + "types" + ], + [ + "missing", + "rule" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_KEY", + "description": "Key cutting function group" + }, + { + "type": "FUNC", + "name": "{{P}}KEY_SAMPLE", + "functionGroup": "{{P}}FG_KEY", + "file": "seed/key_sample.func.abap", + "description": "Sample key module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}KEY_PRICE", + "functionGroup": "{{P}}FG_KEY", + "params": [ + { + "name": "iv_key_type", + "type": "string" + }, + { + "name": "iv_quantity", + "type": "i" + }, + { + "name": "ev_price", + "type": "decfloat34" + }, + { + "name": "ev_valid", + "type": "abap_bool" + } + ] + } + ], + "out_of_scope": [ + "{{P}}KEY_SAMPLE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}KEY_PRICE", + "functionGroup": "{{P}}FG_KEY", + "file": "reference/key_price.func.abap", + "description": "Key price module" + }, + { + "type": "CLAS", + "name": "{{P}}KEY_TEST", + "file": "reference/key_test.clas.abap", + "description": "Own tests for key price" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1128/generation.json b/tasks_gen/train/G1128/generation.json new file mode 100644 index 0000000..a04c0bd --- /dev/null +++ b/tasks_gen/train/G1128/generation.json @@ -0,0 +1,37 @@ +{ + "id": "G1128", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1128/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1128/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..38cc024 --- /dev/null +++ b/tasks_gen/train/G1128/hidden/t16_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}t16_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS all_routes FOR TESTING. + METHODS depot_data FOR TESTING. + METHODS route_without_depot FOR TESTING. + METHODS route_values FOR TESTING. + METHODS stops_of_route FOR TESTING. + METHODS stops_filtered FOR TESTING. + METHODS route_without_stops FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROUTE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_depot TYPE STANDARD TABLE OF {{p}}depot WITH EMPTY KEY. + DATA lt_route TYPE STANDARD TABLE OF {{p}}route WITH EMPTY KEY. + DATA lt_stop TYPE STANDARD TABLE OF {{p}}stop WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_depot = VALUE #( ( depot_id = 'D1' depot_name = 'North' city = 'Leeds' ) + ( depot_id = 'D2' depot_name = 'South' city = 'Bristol' ) ). + lt_route = VALUE #( ( route_id = 'R1' depot_id = 'D1' route_name = 'City Line' distance_km = '12.5' ) + ( route_id = 'R2' depot_id = 'D2' route_name = 'Coast Line' distance_km = '30.0' ) + ( route_id = 'R3' depot_id = 'D9' route_name = 'Hill Line' distance_km = '8.0' ) ). + lt_stop = VALUE #( ( route_id = 'R1' stop_no = 1 stop_name = 'Market' distance_km = '0.0' ) + ( route_id = 'R1' stop_no = 2 stop_name = 'Station' distance_km = '4.2' ) + ( route_id = 'R1' stop_no = 3 stop_name = 'Airport' distance_km = '12.5' ) + ( route_id = 'R2' stop_no = 1 stop_name = 'Harbour' distance_km = '0.0' ) + ( route_id = 'R2' stop_no = 2 stop_name = 'Beach' distance_km = '9.0' ) ). + go_env->insert_test_data( lt_depot ). + go_env->insert_test_data( lt_route ). + go_env->insert_test_data( lt_stop ). + ENDMETHOD. + + METHOD all_routes. + SELECT COUNT(*) FROM {{p}}i_route INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. + + METHOD depot_data. + SELECT SINGLE depot_name, city FROM {{p}}i_route WHERE route_id = 'R1' INTO @DATA(ls_route). + cl_abap_unit_assert=>assert_equals( exp = 'North' act = ls_route-depot_name ). + cl_abap_unit_assert=>assert_equals( exp = 'Leeds' act = ls_route-city ). + ENDMETHOD. + + METHOD route_without_depot. + SELECT SINGLE depot_name, city FROM {{p}}i_route WHERE route_id = 'R3' INTO @DATA(ls_route). + cl_abap_unit_assert=>assert_initial( act = ls_route-depot_name ). + cl_abap_unit_assert=>assert_initial( act = ls_route-city ). + SELECT COUNT(*) FROM {{p}}i_route WHERE route_id = 'R3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD route_values. + SELECT SINGLE route_name, distance_km FROM {{p}}i_route WHERE route_id = 'R2' + INTO @DATA(ls_route). + cl_abap_unit_assert=>assert_equals( exp = 'Coast Line' act = ls_route-route_name ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '30.0' ) + act = CONV decfloat34( ls_route-distance_km ) ). + ENDMETHOD. + + METHOD stops_of_route. + SELECT FROM {{p}}i_route AS rt + INNER JOIN rt\_stop AS st ON st~route_id = rt~route_id + FIELDS rt~route_id, st~stop_no + WHERE rt~route_id = 'R1' + INTO TABLE @DATA(lt_stops). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_stops ) ). + ENDMETHOD. + + METHOD stops_filtered. + SELECT FROM {{p}}i_route AS rt + INNER JOIN rt\_stop AS st ON st~route_id = rt~route_id + FIELDS st~stop_name + WHERE st~distance_km > 5 + INTO TABLE @DATA(lt_names). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_names ) ). + ENDMETHOD. + + METHOD route_without_stops. + SELECT FROM {{p}}i_route AS rt + INNER JOIN rt\_stop AS st ON st~route_id = rt~route_id + FIELDS st~stop_no + WHERE rt~route_id = 'R3' + INTO TABLE @DATA(lt_stops). + cl_abap_unit_assert=>assert_initial( act = lt_stops ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1128/mutation.json b/tasks_gen/train/G1128/mutation.json new file mode 100644 index 0000000..a2b65d3 --- /dev/null +++ b/tasks_gen/train/G1128/mutation.json @@ -0,0 +1,47 @@ +{ + "task": "T16", + "mutants": [ + { + "object": "{{P}}I_ROUTE", + "mutant": "line 5: left outer join -> inner join (join)", + "status": "killed", + "hidden": "2/7", + "failed_tests": [ + "ALL_ROUTES", + "ROUTE_WITHOUT_DEPOT" + ] + }, + { + "object": "{{P}}I_ROUTE", + "mutant": "line 5: d.depot_id = r.depot_id -> d.depot_id = r.route_id (eq)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "DEPOT_DATA" + ] + }, + { + "object": "{{P}}I_ROUTE", + "mutant": "line 11: d.city as city -> d.depot_name as city (element)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "DEPOT_DATA" + ] + }, + { + "object": "{{P}}I_ROUTE", + "mutant": "line 4: to {{P}}I_STOP as _stop -> to {{P}}I_STOP as _stops (assoc)", + "status": "killed", + "hidden": "2/7", + "failed_tests": [ + "STOPS_OF_ROUTE", + "ROUTE_WITHOUT_STOPS" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1128/reference/i_route.ddls.asddls b/tasks_gen/train/G1128/reference/i_route.ddls.asddls new file mode 100644 index 0000000..1df67ab --- /dev/null +++ b/tasks_gen/train/G1128/reference/i_route.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bus routes with depot and stops' +define view entity {{P}}I_ROUTE + as select from {{p}}route as r + left outer join {{p}}depot as d on d.depot_id = r.depot_id + association [0..*] to {{P}}I_STOP as _stop on $projection.route_id = _stop.route_id +{ + key r.route_id as route_id, + r.route_name as route_name, + d.depot_name as depot_name, + d.city as city, + r.distance_km as distance_km, + _stop +} diff --git a/tasks_gen/train/G1128/reference/i_stop.ddls.asddls b/tasks_gen/train/G1128/reference/i_stop.ddls.asddls new file mode 100644 index 0000000..4c63977 --- /dev/null +++ b/tasks_gen/train/G1128/reference/i_stop.ddls.asddls @@ -0,0 +1,10 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Stops of a route' +define view entity {{P}}I_STOP + as select from {{p}}stop +{ + key route_id as route_id, + key stop_no as stop_no, + stop_name as stop_name, + distance_km as distance_km +} diff --git a/tasks_gen/train/G1128/reference/t16_test.clas.abap b/tasks_gen/train/G1128/reference/t16_test.clas.abap new file mode 100644 index 0000000..84e3f36 --- /dev/null +++ b/tasks_gen/train/G1128/reference/t16_test.clas.abap @@ -0,0 +1,51 @@ +CLASS {{p}}t16_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS route_with_stops FOR TESTING. + METHODS missing_depot FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROUTE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_depot TYPE STANDARD TABLE OF {{p}}depot WITH EMPTY KEY. + DATA lt_route TYPE STANDARD TABLE OF {{p}}route WITH EMPTY KEY. + DATA lt_stop TYPE STANDARD TABLE OF {{p}}stop WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_depot = VALUE #( ( depot_id = 'D1' depot_name = 'North' city = 'Leeds' ) ). + lt_route = VALUE #( ( route_id = 'R1' depot_id = 'D1' route_name = 'City Line' distance_km = '12.5' ) ). + lt_stop = VALUE #( ( route_id = 'R1' stop_no = 1 stop_name = 'Market' distance_km = '0.0' ) + ( route_id = 'R1' stop_no = 2 stop_name = 'Station' distance_km = '4.2' ) ). + go_env->insert_test_data( lt_depot ). + go_env->insert_test_data( lt_route ). + go_env->insert_test_data( lt_stop ). + ENDMETHOD. + + METHOD route_with_stops. + SELECT FROM {{p}}i_route AS rt + INNER JOIN rt\_stop AS st ON st~route_id = rt~route_id + FIELDS rt~depot_name, st~stop_name + INTO TABLE @DATA(lt_result). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD missing_depot. + DATA lt_route TYPE STANDARD TABLE OF {{p}}route WITH EMPTY KEY. + lt_route = VALUE #( ( route_id = 'R9' depot_id = 'D9' route_name = 'Hill Line' distance_km = '8.0' ) ). + go_env->insert_test_data( lt_route ). + SELECT SINGLE depot_name FROM {{p}}i_route WHERE route_id = 'R9' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_initial( act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1128/seed/depot.tabl.asabap b/tasks_gen/train/G1128/seed/depot.tabl.asabap new file mode 100644 index 0000000..48cd080 --- /dev/null +++ b/tasks_gen/train/G1128/seed/depot.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Bus depot' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}depot { + key client : abap.clnt not null; + key depot_id : abap.char(4) not null; + depot_name : abap.char(30); + city : abap.char(20); +} diff --git a/tasks_gen/train/G1128/seed/route.tabl.asabap b/tasks_gen/train/G1128/seed/route.tabl.asabap new file mode 100644 index 0000000..0e729ac --- /dev/null +++ b/tasks_gen/train/G1128/seed/route.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Bus route' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}route { + key client : abap.clnt not null; + key route_id : abap.char(6) not null; + depot_id : abap.char(4); + route_name : abap.char(30); + distance_km : abap.dec(6,1); +} diff --git a/tasks_gen/train/G1128/seed/stop.tabl.asabap b/tasks_gen/train/G1128/seed/stop.tabl.asabap new file mode 100644 index 0000000..0a0edd0 --- /dev/null +++ b/tasks_gen/train/G1128/seed/stop.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Stop of a route' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}stop { + key client : abap.clnt not null; + key route_id : abap.char(6) not null; + key stop_no : abap.int4 not null; + stop_name : abap.char(30); + distance_km : abap.dec(6,1); +} diff --git a/tasks_gen/train/G1128/spec.md b/tasks_gen/train/G1128/spec.md new file mode 100644 index 0000000..82deca0 --- /dev/null +++ b/tasks_gen/train/G1128/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A regional bus company operates depots and routes. Show each route together +with the depot that operates it. A report and an OData service read the routes. +The OData service also reads the stops of a route. It uses the association of +the view in an ABAP SQL path expression. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DEPOT (bus depot), package $TMP. Key: DEPOT_ID (CHAR 4). + Fields: DEPOT_NAME (CHAR 30), CITY (CHAR 20). +- Table {{P}}ROUTE (bus route), package $TMP. Key: ROUTE_ID (CHAR 6). + Fields: DEPOT_ID (CHAR 4), ROUTE_NAME (CHAR 30), DISTANCE_KM (DEC 6,1). +- Table {{P}}STOP (stop of a route), package $TMP. Key: ROUTE_ID (CHAR 6), + STOP_NO (INT4). Fields: STOP_NAME (CHAR 30), DISTANCE_KM (DEC 6,1). + +# 4. Contract +- Create the CDS view entity {{P}}I_STOP in package $TMP. + Elements, with these names: ROUTE_ID (key), STOP_NO (key), STOP_NAME, + DISTANCE_KM. +- Create the CDS view entity {{P}}I_ROUTE in package $TMP. + Elements, with these names: ROUTE_ID (key), ROUTE_NAME, DEPOT_NAME, CITY, + DISTANCE_KM, _STOP. +- {{P}}I_ROUTE has the association _STOP. The target of _STOP is the view + entity {{P}}I_STOP. _STOP is an element of {{P}}I_ROUTE, so that ABAP SQL + can use a path expression on _STOP. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. {{P}}I_ROUTE shows one row for each route in {{P}}ROUTE. +2. ROUTE_NAME and DISTANCE_KM are the values of the route. +3. DEPOT_NAME and CITY are the values of the depot of the route. The depot of + a route is the depot with the same DEPOT_ID as the route. +4. A route whose DEPOT_ID is not in {{P}}DEPOT still appears in the view. In + this row DEPOT_NAME and CITY are empty. +5. _STOP links a route to the rows of {{P}}I_STOP that have the same ROUTE_ID + as the route. A route without stops still appears in the view. +6. {{P}}I_STOP shows one row for each row of {{P}}STOP, with the same element + values. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Out of scope: do not change {{P}}DEPOT, {{P}}ROUTE and {{P}}STOP. +- A consumer reads the stops of a route with an ABAP SQL path expression that + uses the association _STOP. + +# 7. Acceptance +- The views are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1128/task.json b/tasks_gen/train/G1128/task.json new file mode 100644 index 0000000..945c3d7 --- /dev/null +++ b/tasks_gen/train/G1128/task.json @@ -0,0 +1,90 @@ +{ + "id": "G1128", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DEPOT", + "file": "seed/depot.tabl.asabap", + "description": "Bus depot" + }, + { + "type": "TABL", + "name": "{{P}}ROUTE", + "file": "seed/route.tabl.asabap", + "description": "Bus route" + }, + { + "type": "TABL", + "name": "{{P}}STOP", + "file": "seed/stop.tabl.asabap", + "description": "Stop of a route" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_STOP", + "fields": [ + "ROUTE_ID", + "STOP_NO", + "STOP_NAME", + "DISTANCE_KM" + ] + }, + { + "type": "DDLS", + "name": "{{P}}I_ROUTE", + "fields": [ + "ROUTE_ID", + "ROUTE_NAME", + "DEPOT_NAME", + "CITY", + "DISTANCE_KM", + "_STOP" + ] + } + ], + "out_of_scope": [ + "{{P}}DEPOT", + "{{P}}ROUTE", + "{{P}}STOP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_STOP", + "file": "reference/i_stop.ddls.asddls", + "description": "Stops of a route" + }, + { + "type": "DDLS", + "name": "{{P}}I_ROUTE", + "file": "reference/i_route.ddls.asddls", + "description": "Bus routes with depot data and the stop association" + }, + { + "type": "CLAS", + "name": "{{P}}T16_TEST", + "file": "reference/t16_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1129/generation.json b/tasks_gen/train/G1129/generation.json new file mode 100644 index 0000000..e261c6b --- /dev/null +++ b/tasks_gen/train/G1129/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1129", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "seed/mealbk.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/mealbk.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 141: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1129/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1129/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..0f1bbb9 --- /dev/null +++ b/tasks_gen/train/G1129/hidden/t14_hidden.clas.abap @@ -0,0 +1,325 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS prepare + IMPORTING iv_week TYPE string + it_book TYPE STANDARD TABLE OF {{P}}MEALBK. + METHODS cleanup + IMPORTING iv_week TYPE string. + METHODS bill + IMPORTING iv_week TYPE string + EXPORTING ev_meals TYPE decfloat34 + ev_pupils TYPE decfloat34 + ev_amount TYPE decfloat34 + ev_discount TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS empty_week_zero FOR TESTING. + METHODS sums_two_pupils FOR TESTING. + METHODS skips_cancelled FOR TESTING. + METHODS skips_free_meal FOR TESTING. + METHODS discount_from_five FOR TESTING. + METHODS no_discount_for_four FOR TESTING. + METHODS discount_is_per_pupil FOR TESTING. + METHODS rejects_negative_price FOR TESTING. + METHODS counts_pupils FOR TESTING. + METHODS rounds_half_up FOR TESTING. + METHODS ignores_other_weeks FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD prepare. + cleanup( iv_week ). + INSERT {{P}}MEALBK FROM TABLE @it_book. + ENDMETHOD. + + METHOD cleanup. + DELETE FROM {{P}}MEALBK WHERE week_key = @iv_week. + ENDMETHOD. + + METHOD bill. + CALL FUNCTION '{{P}}MEAL_BILL' + EXPORTING + iv_week = iv_week + IMPORTING + ev_meals = ev_meals + ev_pupils = ev_pupils + ev_amount = ev_amount + ev_discount = ev_discount + EXCEPTIONS + invalid_price = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD empty_week_zero. + DATA lv_week TYPE string VALUE 'H-EMPTY'. + + cleanup( lv_week ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_discount ). + ENDMETHOD. + + METHOD sums_two_pupils. + DATA lv_week TYPE string VALUE 'H-SUM1'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-SUM1' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.50' ) + ( week_key = 'H-SUM1' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '3.00' ) + ( week_key = 'H-SUM1' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '4.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 2 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '9.50' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD skips_cancelled. + DATA lv_week TYPE string VALUE 'H-CANC'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-CANC' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.00' ) + ( week_key = 'H-CANC' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '5.00' cancelled = 'X' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.00' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD skips_free_meal. + DATA lv_week TYPE string VALUE 'H-FREE'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-FREE' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '0.00' ) + ( week_key = 'H-FREE' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '3.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.00' ) act = lv_amount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD discount_from_five. + DATA lv_week TYPE string VALUE 'H-DISC'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-DISC' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-DISC' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-DISC' pupil = 'A' meal_no = 3 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-DISC' pupil = 'A' meal_no = 4 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-DISC' pupil = 'A' meal_no = 5 meal = 'LUNCH' price = '9.99' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '44.96' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.99' ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD no_discount_for_four. + DATA lv_week TYPE string VALUE 'H-NODS'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-NODS' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-NODS' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-NODS' pupil = 'A' meal_no = 3 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'H-NODS' pupil = 'A' meal_no = 4 meal = 'LUNCH' price = '9.99' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '39.96' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD discount_is_per_pupil. + DATA lv_week TYPE string VALUE 'H-PERP'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-PERP' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'A' meal_no = 3 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'A' meal_no = 4 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'A' meal_no = 5 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'B' meal_no = 2 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'B' meal_no = 3 meal = 'LUNCH' price = '10.00' ) + ( week_key = 'H-PERP' pupil = 'B' meal_no = 4 meal = 'LUNCH' price = '10.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 9 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 2 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '85.00' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.00' ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD rejects_negative_price. + DATA lv_week TYPE string VALUE 'H-NEG'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-NEG' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.00' ) + ( week_key = 'H-NEG' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '-1.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD counts_pupils. + DATA lv_week TYPE string VALUE 'H-PUPL'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-PUPL' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.00' ) + ( week_key = 'H-PUPL' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '3.00' cancelled = 'X' ) + ( week_key = 'H-PUPL' pupil = 'C' meal_no = 1 meal = 'LUNCH' price = '0.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.00' ) act = lv_amount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD rounds_half_up. + DATA lv_week TYPE string VALUE 'H-RND'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-RND' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '1.01' ) + ( week_key = 'H-RND' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '1.01' ) + ( week_key = 'H-RND' pupil = 'A' meal_no = 3 meal = 'LUNCH' price = '1.01' ) + ( week_key = 'H-RND' pupil = 'A' meal_no = 4 meal = 'LUNCH' price = '1.01' ) + ( week_key = 'H-RND' pupil = 'A' meal_no = 5 meal = 'LUNCH' price = '1.01' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.55' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.50' ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD ignores_other_weeks. + DATA lv_week TYPE string VALUE 'H-OTHR'. + DATA lv_other TYPE string VALUE 'H-OTHR2'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'H-OTHR' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.00' ) ) ). + + prepare( iv_week = lv_other + it_book = VALUE #( + ( week_key = 'H-OTHR2' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '9.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) + ev_subrc = DATA(lv_rc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.00' ) act = lv_amount ). + + cleanup( lv_week ). + cleanup( lv_other ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1129/reference/meal_bill.func.abap b/tasks_gen/train/G1129/reference/meal_bill.func.abap new file mode 100644 index 0000000..882fc81 --- /dev/null +++ b/tasks_gen/train/G1129/reference/meal_bill.func.abap @@ -0,0 +1,56 @@ +FUNCTION {{p}}meal_bill + IMPORTING + iv_week TYPE string + EXPORTING + ev_meals TYPE decfloat34 + ev_pupils TYPE decfloat34 + ev_amount TYPE decfloat34 + ev_discount TYPE decfloat34 + EXCEPTIONS + invalid_price. + + TYPES: + BEGIN OF ty_pupil_sum, + pupil TYPE string, + meals TYPE i, + gross TYPE decfloat34, + amount TYPE decfloat34, + END OF ty_pupil_sum. + + DATA lt_pupil TYPE SORTED TABLE OF ty_pupil_sum WITH UNIQUE KEY pupil. + + SELECT pupil, price, cancelled + FROM {{P}}MEALBK + WHERE week_key = @iv_week + INTO TABLE @DATA(lt_book). + + LOOP AT lt_book INTO DATA(ls_book). + IF ls_book-price < 0. + RAISE invalid_price. + ENDIF. + IF ls_book-cancelled = 'X' OR ls_book-price = 0. + CONTINUE. + ENDIF. + READ TABLE lt_pupil ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pupil = ls_book-pupil. + IF sy-subrc <> 0. + INSERT VALUE #( pupil = ls_book-pupil ) INTO TABLE lt_pupil ASSIGNING . + ENDIF. + -meals = -meals + 1. + -gross = -gross + ls_book-price. + ENDLOOP. + + LOOP AT lt_pupil ASSIGNING . + -amount = -gross. + IF -meals >= 5. + -amount = round( val = -gross * 9 / 10 + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ev_meals = ev_meals + -meals. + ev_amount = ev_amount + -amount. + ev_discount = ev_discount + -gross - -amount. + ENDLOOP. + + ev_pupils = lines( lt_pupil ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1129/reference/t14_test.clas.abap b/tasks_gen/train/G1129/reference/t14_test.clas.abap new file mode 100644 index 0000000..f5c9c7f --- /dev/null +++ b/tasks_gen/train/G1129/reference/t14_test.clas.abap @@ -0,0 +1,135 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS prepare + IMPORTING iv_week TYPE string + it_book TYPE STANDARD TABLE OF {{P}}MEALBK. + METHODS cleanup + IMPORTING iv_week TYPE string. + METHODS bill + IMPORTING iv_week TYPE string + EXPORTING ev_meals TYPE decfloat34 + ev_pupils TYPE decfloat34 + ev_amount TYPE decfloat34 + ev_discount TYPE decfloat34. + METHODS sums_two_pupils FOR TESTING. + METHODS skips_cancelled FOR TESTING. + METHODS discount_from_five FOR TESTING. + METHODS rejects_negative_price FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD prepare. + cleanup( iv_week ). + INSERT {{P}}MEALBK FROM TABLE @it_book. + ENDMETHOD. + + METHOD cleanup. + DELETE FROM {{P}}MEALBK WHERE week_key = @iv_week. + ENDMETHOD. + + METHOD bill. + CALL FUNCTION '{{P}}MEAL_BILL' + EXPORTING + iv_week = iv_week + IMPORTING + ev_meals = ev_meals + ev_pupils = ev_pupils + ev_amount = ev_amount + ev_discount = ev_discount + EXCEPTIONS + invalid_price = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD sums_two_pupils. + DATA lv_week TYPE string VALUE 'R-SUM1'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'R-SUM1' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.50' ) + ( week_key = 'R-SUM1' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '3.00' ) + ( week_key = 'R-SUM1' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '4.00' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) ). + + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 2 ) act = lv_pupils ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '9.50' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD skips_cancelled. + DATA lv_week TYPE string VALUE 'R-CANC'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'R-CANC' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.00' ) + ( week_key = 'R-CANC' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '5.00' cancelled = 'X' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) ). + + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 1 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.00' ) act = lv_amount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD discount_from_five. + DATA lv_week TYPE string VALUE 'R-DISC'. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'R-DISC' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'R-DISC' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'R-DISC' pupil = 'A' meal_no = 3 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'R-DISC' pupil = 'A' meal_no = 4 meal = 'LUNCH' price = '9.99' ) + ( week_key = 'R-DISC' pupil = 'A' meal_no = 5 meal = 'LUNCH' price = '9.99' ) ) ). + + bill( EXPORTING iv_week = lv_week + IMPORTING ev_meals = DATA(lv_meals) + ev_pupils = DATA(lv_pupils) + ev_amount = DATA(lv_amount) + ev_discount = DATA(lv_discount) ). + + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = lv_meals ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '44.96' ) act = lv_amount ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.99' ) act = lv_discount ). + + cleanup( lv_week ). + ENDMETHOD. + + METHOD rejects_negative_price. + DATA lv_week TYPE string VALUE 'R-NEG'. + DATA lv_amount TYPE decfloat34. + + prepare( iv_week = lv_week + it_book = VALUE #( + ( week_key = 'R-NEG' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '-1.00' ) ) ). + + CALL FUNCTION '{{P}}MEAL_BILL' + EXPORTING + iv_week = lv_week + IMPORTING + ev_amount = lv_amount + EXCEPTIONS + invalid_price = 1 + OTHERS = 2. + + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + + cleanup( lv_week ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1129/seed/meal_data.clas.abap b/tasks_gen/train/G1129/seed/meal_data.clas.abap new file mode 100644 index 0000000..cd8d125 --- /dev/null +++ b/tasks_gen/train/G1129/seed/meal_data.clas.abap @@ -0,0 +1,23 @@ +CLASS {{p}}meal_data DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}meal_data IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_book TYPE STANDARD TABLE OF {{P}}MEALBK. + + DELETE FROM {{P}}MEALBK. + + lt_book = VALUE #( + ( week_key = '2024-15' pupil = 'A' meal_no = 1 meal = 'LUNCH' price = '2.50' ) + ( week_key = '2024-15' pupil = 'A' meal_no = 2 meal = 'LUNCH' price = '3.00' ) + ( week_key = '2024-15' pupil = 'B' meal_no = 1 meal = 'LUNCH' price = '4.00' ) + ( week_key = '2024-15' pupil = 'C' meal_no = 1 meal = 'LUNCH' price = '5.00' cancelled = 'X' ) ). + + INSERT {{P}}MEALBK FROM TABLE @lt_book. + + out->write( |{ lines( lt_book ) } bookings inserted| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1129/seed/mealbk.tabl.abap b/tasks_gen/train/G1129/seed/mealbk.tabl.abap new file mode 100644 index 0000000..e1ce998 --- /dev/null +++ b/tasks_gen/train/G1129/seed/mealbk.tabl.abap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Meal bookings of the canteen' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}mealbk { + key mandt : abap.clnt not null; + key week_key : abap.char(10) not null; + key pupil : abap.char(10) not null; + key meal_no : abap.int4 not null; + meal : abap.char(10); + price : abap.dec(9,2); + cancelled : abap.char(1); +} diff --git a/tasks_gen/train/G1129/spec.md b/tasks_gen/train/G1129/spec.md new file mode 100644 index 0000000..4cf2d77 --- /dev/null +++ b/tasks_gen/train/G1129/spec.md @@ -0,0 +1,60 @@ +# 1. Goal +A school canteen bills the parents for the meals of one week. The billing +program calls one function module for each week. The function module reads the +meal bookings of the week and returns the number of charged meals, the number +of pupils, the total amount, and the total discount. + +# 2. Open questions +None. + +# 3. Context +- The function group {{P}}FG_MEAL exists in package $TMP. +- The database table {{P}}MEALBK exists in package $TMP. It contains the meal + bookings. Its fields are: + - mandt: the client + - week_key: the week of the booking, for example '2024-15' + - pupil: the pupil who booked the meal + - meal_no: the number of the booking inside the week + - meal: the kind of meal, for example 'LUNCH' + - price: the price of the meal + - cancelled: 'X' when the booking was cancelled, otherwise initial +- The class {{P}}MEAL_DATA fills the table with sample bookings. + +# 4. Contract +- Create the function module {{P}}MEAL_BILL in function group {{P}}FG_MEAL. +- Interface of {{P}}MEAL_BILL: + - IMPORTING iv_week TYPE string + - EXPORTING ev_meals TYPE decfloat34 + - EXPORTING ev_pupils TYPE decfloat34 + - EXPORTING ev_amount TYPE decfloat34 + - EXPORTING ev_discount TYPE decfloat34 + - EXCEPTIONS invalid_price + +# 5. Business rules +1. The function module reads the bookings of the given week. A booking belongs + to the week that is in its field week_key. +2. A booking is charged when cancelled is not 'X' and price is greater than 0. +3. If a booking of the week has a price less than 0, raise the exception + INVALID_PRICE. +4. Each charged booking counts as one meal of its pupil. +5. The gross amount of a pupil is the sum of the prices of the charged bookings + of the pupil. +6. If a pupil has 5 or more charged meals, the amount of the pupil is 90 % of + the gross amount, rounded half up to 2 decimals. If a pupil has fewer than 5 + charged meals, the amount of the pupil is the gross amount. +7. ev_meals is the total number of charged meals of the week. +8. ev_pupils is the number of pupils with at least one charged booking. +9. ev_amount is the sum of the amounts of all pupils. +10. ev_discount is the sum of the differences between the gross amount and the + amount of each pupil. +11. If the week has no charged booking, all output parameters are 0. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}MEALBK and {{P}}MEAL_DATA. + +# 7. Acceptance +- The function module {{P}}MEAL_BILL is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1129/task.json b/tasks_gen/train/G1129/task.json new file mode 100644 index 0000000..6234bdd --- /dev/null +++ b/tasks_gen/train/G1129/task.json @@ -0,0 +1,91 @@ +{ + "id": "G1129", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}MEALBK", + "file": "seed/mealbk.tabl.abap", + "description": "Meal bookings of the canteen" + }, + { + "type": "CLAS", + "name": "{{P}}MEAL_DATA", + "file": "seed/meal_data.clas.abap", + "description": "Sample meal bookings", + "run": true + }, + { + "type": "FUGR", + "name": "{{P}}FG_MEAL", + "description": "Canteen billing" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}MEAL_BILL", + "functionGroup": "{{P}}FG_MEAL", + "params": [ + { + "name": "iv_week", + "type": "string" + }, + { + "name": "ev_meals", + "type": "decfloat34" + }, + { + "name": "ev_pupils", + "type": "decfloat34" + }, + { + "name": "ev_amount", + "type": "decfloat34" + }, + { + "name": "ev_discount", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}MEALBK", + "{{P}}MEAL_DATA" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}MEAL_BILL", + "functionGroup": "{{P}}FG_MEAL", + "file": "reference/meal_bill.func.abap", + "description": "Canteen billing module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1130/faulty/m0_groom_bill_old.prog.abap b/tasks_gen/train/G1130/faulty/m0_groom_bill_old.prog.abap new file mode 100644 index 0000000..25826ae --- /dev/null +++ b/tasks_gen/train/G1130/faulty/m0_groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + OR pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat = c_coat_long. + rv_charge = rv_charge + c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg > c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full AND is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/faulty/m1_groom_bill_old.prog.abap b/tasks_gen/train/G1130/faulty/m1_groom_bill_old.prog.abap new file mode 100644 index 0000000..fcfc8c0 --- /dev/null +++ b/tasks_gen/train/G1130/faulty/m1_groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + AND pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat <> c_coat_long. + rv_charge = rv_charge + c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg > c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full AND is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/faulty/m2_groom_bill_old.prog.abap b/tasks_gen/train/G1130/faulty/m2_groom_bill_old.prog.abap new file mode 100644 index 0000000..b969b18 --- /dev/null +++ b/tasks_gen/train/G1130/faulty/m2_groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + AND pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg > c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full AND is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/faulty/m3_groom_bill_old.prog.abap b/tasks_gen/train/G1130/faulty/m3_groom_bill_old.prog.abap new file mode 100644 index 0000000..9d5bb9b --- /dev/null +++ b/tasks_gen/train/G1130/faulty/m3_groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + AND pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat = c_coat_long. + rv_charge = rv_charge + c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg <= c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full AND is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/faulty/m4_groom_bill_old.prog.abap b/tasks_gen/train/G1130/faulty/m4_groom_bill_old.prog.abap new file mode 100644 index 0000000..807b865 --- /dev/null +++ b/tasks_gen/train/G1130/faulty/m4_groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + AND pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat = c_coat_long. + rv_charge = rv_charge + c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg > c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full OR is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/generation.json b/tasks_gen/train/G1130/generation.json new file mode 100644 index 0000000..80a5cc9 --- /dev/null +++ b/tasks_gen/train/G1130/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1130", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1130/hidden/groom_hidden.clas.abap b/tasks_gen/train/G1130/hidden/groom_hidden.clas.abap new file mode 100644 index 0000000..2ba50a1 --- /dev/null +++ b/tasks_gen/train/G1130/hidden/groom_hidden.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}groom_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_pet TYPE RANGE OF {{p}}groom-pet. + METHODS run IMPORTING iv_date TYPE d it_pet TYPE tt_pet OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of IMPORTING ir_data TYPE REF TO data iv_row TYPE i iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of IMPORTING ir_data TYPE REF TO data RETURNING VALUE(rv_lines) TYPE i. + METHODS all_orders FOR TESTING. + METHODS pet_filter FOR TESTING. + METHODS other_date FOR TESTING. + METHODS basic_charge FOR TESTING. + METHODS full_charge FOR TESTING. + METHODS heavy_pet FOR TESTING. + METHODS long_coat FOR TESTING. + METHODS no_orders FOR TESTING. +ENDCLASS. + + +CLASS {{p}}groom_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}groom_bill_old WITH p_date = iv_date WITH s_pet IN it_pet AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_orders. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GROOM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `DOG` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PET` ) ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lr_data) = run( iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `30.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CHARGE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `65.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD other_date. + DATA(lr_data) = run( '20260331' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `40.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD basic_charge. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `BASIC` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SERVICE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `30.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD full_charge. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `65.00` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD heavy_pet. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `65.00` act = value_of( ir_data = lr_data iv_row = 5 iv_col = `CHARGE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `70.00` act = value_of( ir_data = lr_data iv_row = 6 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD long_coat. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `75.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `CHARGE` ) ). + ENDMETHOD. + + METHOD no_orders. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( run( '20260101' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/mutation.json b/tasks_gen/train/G1130/mutation.json new file mode 100644 index 0000000..7fe58db --- /dev/null +++ b/tasks_gen/train/G1130/mutation.json @@ -0,0 +1,72 @@ +{ + "task": "G1130", + "mutants": [ + { + "object": "{{P}}GROOM_BILL_OLD", + "mutant": "line 50: AND -> OR (logic)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "ALL_ORDERS", + "HEAVY_PET", + "NO_ORDERS", + "OTHER_DATE", + "PET_FILTER" + ] + }, + { + "object": "{{P}}GROOM_BILL_OLD", + "mutant": "line 69: = -> <> (eq)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "BASIC_CHARGE", + "FULL_CHARGE", + "HEAVY_PET", + "LONG_COAT", + "OTHER_DATE", + "PET_FILTER" + ] + }, + { + "object": "{{P}}GROOM_BILL_OLD", + "mutant": "line 70: + -> - (arith)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "LONG_COAT", + "OTHER_DATE" + ] + }, + { + "object": "{{P}}GROOM_BILL_OLD", + "mutant": "line 75: > -> <= (rel)", + "status": "killed", + "hidden": "2/8", + "failed_tests": [ + "BASIC_CHARGE", + "FULL_CHARGE", + "HEAVY_PET", + "LONG_COAT", + "OTHER_DATE", + "PET_FILTER" + ] + }, + { + "object": "{{P}}GROOM_BILL_OLD", + "mutant": "line 79: AND -> OR (logic)", + "status": "killed", + "hidden": "4/8", + "failed_tests": [ + "FULL_CHARGE", + "HEAVY_PET", + "OTHER_DATE", + "PET_FILTER" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1130/reference/groom_bill_old.prog.abap b/tasks_gen/train/G1130/reference/groom_bill_old.prog.abap new file mode 100644 index 0000000..37a0453 --- /dev/null +++ b/tasks_gen/train/G1130/reference/groom_bill_old.prog.abap @@ -0,0 +1,137 @@ +REPORT {{p}}groom_bill_old. + +TYPES: ty_charge TYPE p LENGTH 9 DECIMALS 2, + BEGIN OF ty_line, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE ty_charge, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_pet TYPE RANGE OF {{p}}groom-pet. + +DATA gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +CLASS lcl_groom_bill DEFINITION FINAL. + PUBLIC SECTION. + CONSTANTS: c_service_basic TYPE {{p}}groom-service VALUE 'BASIC', + c_service_full TYPE {{p}}groom-service VALUE 'FULL', + c_coat_long TYPE {{p}}groom-coat VALUE 'LONG', + c_coat_curly TYPE {{p}}groom-coat VALUE 'CURLY', + c_charge_basic TYPE ty_charge VALUE '30.00', + c_charge_full TYPE ty_charge VALUE '50.00', + c_surch_long TYPE ty_charge VALUE '10.00', + c_surch_curly TYPE ty_charge VALUE '15.00', + c_surch_heavy TYPE ty_charge VALUE '20.00', + c_discount TYPE ty_charge VALUE '5.00', + c_heavy_kg TYPE {{p}}groom-weight_kg VALUE '25.0'. + METHODS bill + IMPORTING iv_date TYPE d + it_pet TYPE tt_pet + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS charge_of + IMPORTING is_groom TYPE {{p}}groom + RETURNING VALUE(rv_charge) TYPE ty_charge. +ENDCLASS. + +CLASS lcl_groom_bill IMPLEMENTATION. + METHOD bill. + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + + SELECT * + FROM {{p}}groom + WHERE visit_date = @iv_date + AND pet IN @it_pet + ORDER BY groom_id + INTO TABLE @lt_groom. + + rt_lines = VALUE #( + FOR ls_groom IN lt_groom + ( groom_id = ls_groom-groom_id + pet = ls_groom-pet + service = ls_groom-service + charge = charge_of( ls_groom ) ) ). + ENDMETHOD. + + METHOD charge_of. + IF is_groom-service = c_service_full. + rv_charge = c_charge_full. + ELSEIF is_groom-service = c_service_basic. + rv_charge = c_charge_basic. + ENDIF. + + IF is_groom-coat = c_coat_long. + rv_charge = rv_charge + c_surch_long. + ELSEIF is_groom-coat = c_coat_curly. + rv_charge = rv_charge + c_surch_curly. + ENDIF. + + IF is_groom-weight_kg > c_heavy_kg. + rv_charge = rv_charge + c_surch_heavy. + ENDIF. + + IF is_groom-service = c_service_full AND is_groom-coat = c_coat_long. + rv_charge = rv_charge - c_discount. + ENDIF. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_msg). + MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_bill) = NEW lcl_groom_bill( ). + DATA(gt_lines) = go_bill->bill( iv_date = p_date it_pet = s_pet[] ). + go_bill->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_bill DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS charges FOR TESTING. + METHODS pet_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_bill IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}GROOM' ) ) ). + DATA lt_groom TYPE STANDARD TABLE OF {{p}}groom WITH EMPTY KEY. + lt_groom = VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '10.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'CAT' coat = 'CURLY' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'FULL' visit_date = '20260331' ) ). + go_env->insert_test_data( lt_groom ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD charges. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( iv_date = '20260401' it_pet = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '30.00' act = |{ lt_lines[ 1 ]-charge }| ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 2 ]-charge }| ). + ENDMETHOD. + + METHOD pet_filter. + DATA(lt_lines) = NEW lcl_groom_bill( )->bill( + iv_date = '20260401' it_pet = VALUE #( ( sign = 'I' option = 'EQ' low = 'CAT' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '85.00' act = |{ lt_lines[ 1 ]-charge }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/seed/groom.tabl.asabap b/tasks_gen/train/G1130/seed/groom.tabl.asabap new file mode 100644 index 0000000..8be64b3 --- /dev/null +++ b/tasks_gen/train/G1130/seed/groom.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Grooming orders' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}groom { + key client : abap.clnt not null; + key groom_id : abap.int4 not null; + pet : abap.char(10); + coat : abap.char(8); + weight_kg : abap.dec(5,1); + service : abap.char(6); + visit_date : abap.dats; +} diff --git a/tasks_gen/train/G1130/seed/groom_bill_old.prog.abap b/tasks_gen/train/G1130/seed/groom_bill_old.prog.abap new file mode 100644 index 0000000..b5d2903 --- /dev/null +++ b/tasks_gen/train/G1130/seed/groom_bill_old.prog.abap @@ -0,0 +1,59 @@ +REPORT {{p}}groom_bill_old. + +DATA: gv_pet TYPE {{p}}groom-pet. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_pet FOR gv_pet. + +TYPES: BEGIN OF ty_out, + groom_id TYPE {{p}}groom-groom_id, + pet TYPE {{p}}groom-pet, + service TYPE {{p}}groom-service, + charge TYPE p LENGTH 9 DECIMALS 2, + END OF ty_out. + +DATA: gt_out TYPE STANDARD TABLE OF ty_out, + gs_out TYPE ty_out, + gt_groom TYPE STANDARD TABLE OF {{p}}groom, + gs_groom TYPE {{p}}groom. + +START-OF-SELECTION. + + SELECT * FROM {{p}}groom INTO TABLE gt_groom + WHERE visit_date = p_date AND pet IN s_pet. + + LOOP AT gt_groom INTO gs_groom. + CLEAR gs_out. + gs_out-groom_id = gs_groom-groom_id. + gs_out-pet = gs_groom-pet. + gs_out-service = gs_groom-service. + IF gs_groom-service = 'BASIC'. + gs_out-charge = 30. + ELSEIF gs_groom-service = 'FULL'. + gs_out-charge = 50. + ELSE. + gs_out-charge = 0. + ENDIF. + IF gs_groom-coat = 'LONG'. + gs_out-charge = gs_out-charge + 10. + ELSEIF gs_groom-coat = 'CURLY'. + gs_out-charge = gs_out-charge + 15. + ENDIF. + IF gs_groom-weight_kg > 25. + gs_out-charge = gs_out-charge + 20. + ENDIF. + IF gs_groom-service = 'FULL' AND gs_groom-coat = 'LONG'. + gs_out-charge = gs_out-charge - 5. + ENDIF. + APPEND gs_out TO gt_out. + ENDLOOP. + + SORT gt_out BY groom_id. + + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = gt_out ). + lo_alv->display( ). + CATCH cx_salv_msg. + ENDTRY. diff --git a/tasks_gen/train/G1130/seed/groom_seed.clas.abap b/tasks_gen/train/G1130/seed/groom_seed.clas.abap new file mode 100644 index 0000000..b5eae41 --- /dev/null +++ b/tasks_gen/train/G1130/seed/groom_seed.clas.abap @@ -0,0 +1,19 @@ +CLASS {{p}}groom_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}groom_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}groom. + INSERT {{p}}groom FROM TABLE @( VALUE #( + ( groom_id = 1 pet = 'DOG' coat = 'SHORT' weight_kg = '12.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 2 pet = 'DOG' coat = 'LONG' weight_kg = '30.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 3 pet = 'CAT' coat = 'SHORT' weight_kg = '4.5' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 4 pet = 'CAT' coat = 'CURLY' weight_kg = '6.0' service = 'FULL' visit_date = '20260401' ) + ( groom_id = 5 pet = 'DOG' coat = 'CURLY' weight_kg = '28.0' service = 'BASIC' visit_date = '20260401' ) + ( groom_id = 6 pet = 'CAT' coat = 'LONG' weight_kg = '5.0' service = 'BASIC' visit_date = '20260331' ) + ( groom_id = 7 pet = 'DOG' coat = 'SHORT' weight_kg = '40.0' service = 'FULL' visit_date = '20260401' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1130/spec.md b/tasks_gen/train/G1130/spec.md new file mode 100644 index 0000000..945a2be --- /dev/null +++ b/tasks_gen/train/G1130/spec.md @@ -0,0 +1,41 @@ +# 1. Goal +A pet grooming salon bills its customers for the grooming of their pets. The +salon uses the report {{P}}GROOM_BILL_OLD to see the charge of each grooming +order of one day. + +# 2. Open questions +None. + +# 3. Context +- The legacy report {{P}}GROOM_BILL_OLD exists in package $TMP. It is active. + It reads the table {{P}}GROOM and shows the result with CL_SALV_TABLE. +- The table {{P}}GROOM contains data. Fields: GROOM_ID (INT4, key), + PET (CHAR 10), COAT (CHAR 8), WEIGHT_KG (DEC 5,1), SERVICE (CHAR 6), + VISIT_DATE (DATS). +- The legacy report has a selection screen with the parameter P_DATE and the + select-option S_PET. + +# 4. Contract +- Refactor the report {{P}}GROOM_BILL_OLD in package $TMP. The name stays the + same. +- The selection screen stays the same: + - Parameter P_DATE, type DATS, mandatory: the visit date to include. + - Select-option S_PET for the pet kind. +- The ALV output stays the same. The output table has these columns with these + names: GROOM_ID, PET, SERVICE, CHARGE. + +# 5. Business rules +The behavior of the report stays the same. The rules are in the legacy code of +{{P}}GROOM_BILL_OLD. Read the legacy code and keep every rule. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Read the data with one database access. Keep + every method below 40 statements. Do not use a database access in a loop. +- Out of scope: do not change {{P}}GROOM and {{P}}GROOM_SEED. + +# 7. Acceptance +- The report is active and has no syntax error. +- The behavior of the report is unchanged. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the report. diff --git a/tasks_gen/train/G1130/task.json b/tasks_gen/train/G1130/task.json new file mode 100644 index 0000000..59659aa --- /dev/null +++ b/tasks_gen/train/G1130/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1130", + "category": "E", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}GROOM", + "file": "seed/groom.tabl.asabap", + "description": "Grooming orders of the pet grooming salon" + }, + { + "type": "CLAS", + "name": "{{P}}GROOM_SEED", + "file": "seed/groom_seed.clas.abap", + "description": "Grooming seed data", + "run": true + }, + { + "type": "PROG", + "name": "{{P}}GROOM_BILL_OLD", + "file": "seed/groom_bill_old.prog.abap", + "description": "Legacy grooming bill report" + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}GROOM_BILL_OLD", + "parameters": [ + "p_date", + "s_pet" + ] + } + ], + "out_of_scope": [ + "{{P}}GROOM", + "{{P}}GROOM_SEED" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}GROOM_HIDDEN", + "file": "hidden/groom_hidden.clas.abap", + "description": "Hidden tests for the grooming bill report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}GROOM_BILL_OLD", + "file": "reference/groom_bill_old.prog.abap", + "description": "Refactored grooming bill report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length", + "clean_code_naming" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1131/faulty/m0_i_honey_yield.ddls.asddls b/tasks_gen/train/G1131/faulty/m0_i_honey_yield.ddls.asddls new file mode 100644 index 0000000..a48cc7a --- /dev/null +++ b/tasks_gen/train/G1131/faulty/m0_i_honey_yield.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Honey yield per apiary' +define view entity {{P}}I_HONEY_YIELD + as select from {{p}}apiary as api + left outer join {{p}}honey as harv on harv.apiary_id = api.apiary_id +{ + key api.apiary_id as apiary_id, + api.beekeeper as beekeeper, + api.region as region, + count( * ) as harvest_count, + sum( harv.kilograms ) as total_kg +} +group by + api.apiary_id, + api.beekeeper, + api.region diff --git a/tasks_gen/train/G1131/faulty/m1_i_honey_yield.ddls.asddls b/tasks_gen/train/G1131/faulty/m1_i_honey_yield.ddls.asddls new file mode 100644 index 0000000..ccedb17 --- /dev/null +++ b/tasks_gen/train/G1131/faulty/m1_i_honey_yield.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Honey yield per apiary' +define view entity {{P}}I_HONEY_YIELD + as select from {{p}}apiary as api + inner join {{p}}honey as harv on harv.apiary_id <> api.apiary_id +{ + key api.apiary_id as apiary_id, + api.beekeeper as beekeeper, + api.region as region, + count( * ) as harvest_count, + sum( harv.kilograms ) as total_kg +} +group by + api.apiary_id, + api.beekeeper, + api.region diff --git a/tasks_gen/train/G1131/faulty/m2_i_honey_yield.ddls.asddls b/tasks_gen/train/G1131/faulty/m2_i_honey_yield.ddls.asddls new file mode 100644 index 0000000..8f61d8e --- /dev/null +++ b/tasks_gen/train/G1131/faulty/m2_i_honey_yield.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Honey yield per apiary' +define view entity {{P}}I_HONEY_YIELD + as select from {{p}}apiary as api + inner join {{p}}honey as harv on harv.apiary_id = api.apiary_id +{ + key api.apiary_id as apiary_id, + api.beekeeper as beekeeper, + api.region as region, + count( * ) as harvest_count, + max( harv.kilograms ) as total_kg +} +group by + api.apiary_id, + api.beekeeper, + api.region diff --git a/tasks_gen/train/G1131/generation.json b/tasks_gen/train/G1131/generation.json new file mode 100644 index 0000000..821ae95 --- /dev/null +++ b/tasks_gen/train/G1131/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1131", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1131/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1131/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..d17989f --- /dev/null +++ b/tasks_gen/train/G1131/hidden/t41_hidden.clas.abap @@ -0,0 +1,90 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS yield_of_apiary FOR TESTING. + METHODS harvest_count_of_apiary FOR TESTING. + METHODS apiary_without_harvest FOR TESTING. + METHODS number_of_rows FOR TESTING. + METHODS beekeeper_shown FOR TESTING. + METHODS region_shown FOR TESTING. + METHODS single_harvest_total FOR TESTING. + METHODS quiet_region_apiary FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_HONEY_YIELD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_apiary TYPE STANDARD TABLE OF {{p}}apiary WITH EMPTY KEY. + DATA lt_honey TYPE STANDARD TABLE OF {{p}}honey WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_apiary = VALUE #( + ( apiary_id = 'A1' beekeeper = 'BK1' region = 'NORT' hive_count = 10 ) + ( apiary_id = 'A2' beekeeper = 'BK2' region = 'NORT' hive_count = 5 ) + ( apiary_id = 'A3' beekeeper = 'BK3' region = 'SOUT' hive_count = 8 ) + ( apiary_id = 'A4' beekeeper = 'BK4' region = 'SOUT' hive_count = 3 ) + ( apiary_id = 'A5' beekeeper = 'BK5' region = 'WEST' hive_count = 4 ) ). + lt_honey = VALUE #( + ( apiary_id = 'A1' harvest_no = 1 region = 'NORT' harvest_date = '20240115' kilograms = '12.00' ) + ( apiary_id = 'A1' harvest_no = 2 region = 'NORT' harvest_date = '20240220' kilograms = '8.00' ) + ( apiary_id = 'A2' harvest_no = 1 region = 'NORT' harvest_date = '20240301' kilograms = '20.00' ) + ( apiary_id = 'A3' harvest_no = 1 region = 'SOUT' harvest_date = '20240410' kilograms = '5.00' ) ). + go_env->insert_test_data( lt_apiary ). + go_env->insert_test_data( lt_honey ). + ENDMETHOD. + + METHOD yield_of_apiary. + SELECT SINGLE total_kg FROM {{p}}i_honey_yield WHERE apiary_id = 'A1' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD harvest_count_of_apiary. + SELECT SINGLE harvest_count FROM {{p}}i_honey_yield WHERE apiary_id = 'A1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD apiary_without_harvest. + SELECT COUNT(*) FROM {{p}}i_honey_yield WHERE apiary_id = 'A4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD number_of_rows. + SELECT COUNT(*) FROM {{p}}i_honey_yield INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. + + METHOD beekeeper_shown. + SELECT SINGLE beekeeper FROM {{p}}i_honey_yield WHERE apiary_id = 'A2' INTO @DATA(lv_keeper). + cl_abap_unit_assert=>assert_equals( exp = 'BK2' act = lv_keeper ). + ENDMETHOD. + + METHOD region_shown. + SELECT SINGLE region FROM {{p}}i_honey_yield WHERE apiary_id = 'A3' INTO @DATA(lv_region). + cl_abap_unit_assert=>assert_equals( exp = 'SOUT' act = lv_region ). + ENDMETHOD. + + METHOD single_harvest_total. + SELECT SINGLE harvest_count, total_kg FROM {{p}}i_honey_yield WHERE apiary_id = 'A3' + INTO @DATA(ls_yield). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_yield-harvest_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.00' ) + act = CONV decfloat34( ls_yield-total_kg ) ). + ENDMETHOD. + + METHOD quiet_region_apiary. + SELECT COUNT(*) FROM {{p}}i_honey_yield WHERE apiary_id = 'A5' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1131/mutation.json b/tasks_gen/train/G1131/mutation.json new file mode 100644 index 0000000..17d60e7 --- /dev/null +++ b/tasks_gen/train/G1131/mutation.json @@ -0,0 +1,42 @@ +{ + "task": "G1131", + "mutants": [ + { + "object": "{{P}}I_HONEY_YIELD", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "5/8", + "failed_tests": [ + "APIARY_WITHOUT_HARVEST", + "NUMBER_OF_ROWS", + "QUIET_REGION_APIARY" + ] + }, + { + "object": "{{P}}I_HONEY_YIELD", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "APIARY_WITHOUT_HARVEST", + "NUMBER_OF_ROWS", + "QUIET_REGION_APIARY", + "SINGLE_HARVEST_TOTAL", + "YIELD_OF_APIARY" + ] + }, + { + "object": "{{P}}I_HONEY_YIELD", + "mutant": "line 11: sum( -> max( (agg)", + "status": "killed", + "hidden": "7/8", + "failed_tests": [ + "YIELD_OF_APIARY" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1131/reference/i_honey_yield.ddls.asddls b/tasks_gen/train/G1131/reference/i_honey_yield.ddls.asddls new file mode 100644 index 0000000..3079dbb --- /dev/null +++ b/tasks_gen/train/G1131/reference/i_honey_yield.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Honey yield per apiary' +define view entity {{P}}I_HONEY_YIELD + as select from {{p}}apiary as api + inner join {{p}}honey as harv on harv.apiary_id = api.apiary_id +{ + key api.apiary_id as apiary_id, + api.beekeeper as beekeeper, + api.region as region, + count( * ) as harvest_count, + sum( harv.kilograms ) as total_kg +} +group by + api.apiary_id, + api.beekeeper, + api.region diff --git a/tasks_gen/train/G1131/reference/t41_test.clas.abap b/tasks_gen/train/G1131/reference/t41_test.clas.abap new file mode 100644 index 0000000..85f289f --- /dev/null +++ b/tasks_gen/train/G1131/reference/t41_test.clas.abap @@ -0,0 +1,48 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS total_of_one_apiary FOR TESTING. + METHODS apiary_without_harvest FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_HONEY_YIELD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_apiary TYPE STANDARD TABLE OF {{p}}apiary WITH EMPTY KEY. + DATA lt_honey TYPE STANDARD TABLE OF {{p}}honey WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_apiary = VALUE #( + ( apiary_id = 'B1' beekeeper = 'BK9' region = 'EAST' hive_count = 6 ) + ( apiary_id = 'B2' beekeeper = 'BK8' region = 'EAST' hive_count = 2 ) ). + lt_honey = VALUE #( + ( apiary_id = 'B1' harvest_no = 1 region = 'EAST' harvest_date = '20240501' kilograms = '7.50' ) + ( apiary_id = 'B1' harvest_no = 2 region = 'EAST' harvest_date = '20240601' kilograms = '2.50' ) ). + go_env->insert_test_data( lt_apiary ). + go_env->insert_test_data( lt_honey ). + ENDMETHOD. + + METHOD total_of_one_apiary. + SELECT SINGLE harvest_count, total_kg FROM {{p}}i_honey_yield WHERE apiary_id = 'B1' + INTO @DATA(ls_yield). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_yield-harvest_count ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) + act = CONV decfloat34( ls_yield-total_kg ) ). + ENDMETHOD. + + METHOD apiary_without_harvest. + SELECT COUNT(*) FROM {{p}}i_honey_yield INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1131/seed/apiary.tabl.asabap b/tasks_gen/train/G1131/seed/apiary.tabl.asabap new file mode 100644 index 0000000..b256ee2 --- /dev/null +++ b/tasks_gen/train/G1131/seed/apiary.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Apiary' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}apiary { + key client : abap.clnt not null; + key apiary_id : abap.char(10) not null; + beekeeper : abap.char(10); + region : abap.char(4); + hive_count : abap.int4; +} diff --git a/tasks_gen/train/G1131/seed/honey.tabl.asabap b/tasks_gen/train/G1131/seed/honey.tabl.asabap new file mode 100644 index 0000000..6c8f5ed --- /dev/null +++ b/tasks_gen/train/G1131/seed/honey.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Honey harvest' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}honey { + key client : abap.clnt not null; + key apiary_id : abap.char(10) not null; + key harvest_no : abap.int4 not null; + region : abap.char(4); + harvest_date : abap.dats; + kilograms : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1131/seed/i_honey_yield.ddls.asddls b/tasks_gen/train/G1131/seed/i_honey_yield.ddls.asddls new file mode 100644 index 0000000..b171609 --- /dev/null +++ b/tasks_gen/train/G1131/seed/i_honey_yield.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Honey yield per apiary' +define view entity {{P}}I_HONEY_YIELD + as select from {{p}}apiary as api + inner join {{p}}honey as harv on harv.region = api.region +{ + key api.apiary_id as apiary_id, + api.beekeeper as beekeeper, + api.region as region, + count( * ) as harvest_count, + sum( harv.kilograms ) as total_kg +} +group by + api.apiary_id, + api.beekeeper, + api.region diff --git a/tasks_gen/train/G1131/spec.md b/tasks_gen/train/G1131/spec.md new file mode 100644 index 0000000..f4390ea --- /dev/null +++ b/tasks_gen/train/G1131/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +The CDS view entity {{P}}I_HONEY_YIELD shows the honey yield of each apiary of a +beekeeping cooperative. The view is active, but its numbers are wrong. Fix the +view in place. A report and an OData service read the view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}APIARY (apiary header), package $TMP. Key: APIARY_ID (CHAR 10). + Fields: BEEKEEPER (CHAR 10), REGION (CHAR 4), HIVE_COUNT (INT4). +- Table {{P}}HONEY (honey harvest), package $TMP. Key: APIARY_ID (CHAR 10), + HARVEST_NO (INT4). Fields: REGION (CHAR 4, region of the apiary at the time + of the harvest), HARVEST_DATE (DATS), KILOGRAMS (DEC 15,2). +- The view {{P}}I_HONEY_YIELD is in package $TMP and is active. The name, the + package and the element names stay the same. + +# 4. Contract +- Fix the CDS view entity {{P}}I_HONEY_YIELD in package $TMP. Do not create a + new view. +- Elements, with these names: APIARY_ID (key), BEEKEEPER, REGION, HARVEST_COUNT, + TOTAL_KG. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +The users report these symptoms: +1. TOTAL_KG is too high for several apiaries. Expected: TOTAL_KG is the sum of + the KILOGRAMS of the harvests of that apiary only. +2. HARVEST_COUNT is too high for the same apiaries. Expected: HARVEST_COUNT is + the number of harvest records of that apiary only. +3. The list also shows apiaries that delivered no honey. Expected: the view + shows one row for each apiary that has at least one record in {{P}}HONEY. An + apiary without harvest records is not shown. +All other behavior stays the same. In particular: +4. BEEKEEPER is the beekeeper of the apiary, and REGION is the region of the + apiary. +5. An apiary with harvest records has exactly one row, also when no other apiary + of its region has harvest records. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. The view must be active and must not have a + syntax error. ATC must not report findings of priority 1 or priority 2. +- Out of scope: do not change {{P}}APIARY and {{P}}HONEY. Do not change the name + of the view or the names of its elements. + +# 7. Acceptance +- The view {{P}}I_HONEY_YIELD is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1131/task.json b/tasks_gen/train/G1131/task.json new file mode 100644 index 0000000..574903b --- /dev/null +++ b/tasks_gen/train/G1131/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1131", + "category": "I", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}APIARY", + "file": "seed/apiary.tabl.asabap", + "description": "Apiary header" + }, + { + "type": "TABL", + "name": "{{P}}HONEY", + "file": "seed/honey.tabl.asabap", + "description": "Honey harvest records" + }, + { + "type": "DDLS", + "name": "{{P}}I_HONEY_YIELD", + "file": "seed/i_honey_yield.ddls.asddls", + "description": "Active but faulty honey yield view" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_HONEY_YIELD", + "fields": [ + "APIARY_ID", + "BEEKEEPER", + "REGION", + "HARVEST_COUNT", + "TOTAL_KG" + ] + } + ], + "out_of_scope": [ + "{{P}}APIARY", + "{{P}}HONEY" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_HONEY_YIELD", + "file": "reference/i_honey_yield.ddls.asddls", + "description": "Fixed honey yield view" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "T41 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1132/faulty/m0_walker_stats.prog.abap b/tasks_gen/train/G1132/faulty/m0_walker_stats.prog.abap new file mode 100644 index 0000000..76a293a --- /dev/null +++ b/tasks_gen/train/G1132/faulty/m0_walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + OR walker IN @it_walker + AND status = 'F' + GROUP BY walker + HAVING COUNT( * ) >= 2 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/faulty/m1_walker_stats.prog.abap b/tasks_gen/train/G1132/faulty/m1_walker_stats.prog.abap new file mode 100644 index 0000000..5e6b97f --- /dev/null +++ b/tasks_gen/train/G1132/faulty/m1_walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + AND walker IN @it_walker + OR status = 'F' + GROUP BY walker + HAVING COUNT( * ) >= 2 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/faulty/m2_walker_stats.prog.abap b/tasks_gen/train/G1132/faulty/m2_walker_stats.prog.abap new file mode 100644 index 0000000..6c06f60 --- /dev/null +++ b/tasks_gen/train/G1132/faulty/m2_walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + AND walker IN @it_walker + AND status <> 'F' + GROUP BY walker + HAVING COUNT( * ) >= 2 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/faulty/m3_walker_stats.prog.abap b/tasks_gen/train/G1132/faulty/m3_walker_stats.prog.abap new file mode 100644 index 0000000..839690b --- /dev/null +++ b/tasks_gen/train/G1132/faulty/m3_walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + AND walker IN @it_walker + AND status = 'F' + GROUP BY walker + HAVING COUNT( * ) < 2 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/faulty/m4_walker_stats.prog.abap b/tasks_gen/train/G1132/faulty/m4_walker_stats.prog.abap new file mode 100644 index 0000000..5b0d49e --- /dev/null +++ b/tasks_gen/train/G1132/faulty/m4_walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + AND walker IN @it_walker + AND status = 'F' + GROUP BY walker + HAVING COUNT( * ) >= 3 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/generation.json b/tasks_gen/train/G1132/generation.json new file mode 100644 index 0000000..0a00543 --- /dev/null +++ b/tasks_gen/train/G1132/generation.json @@ -0,0 +1,33 @@ +{ + "id": "G1132", + "pool": "train", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0160 (similarity spec 0.61, rules 0.43, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1132/hidden/walk_check.clas.abap b/tasks_gen/train/G1132/hidden/walk_check.clas.abap new file mode 100644 index 0000000..6da2693 --- /dev/null +++ b/tasks_gen/train/G1132/hidden/walk_check.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}walk_check DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS run + IMPORTING it_date TYPE tt_date OPTIONAL + it_walker TYPE tt_walker OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + + METHODS all_walkers FOR TESTING. + METHODS sorted_by_dist FOR TESTING. + METHODS dist_and_walks FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS walker_filter FOR TESTING. + METHODS cancelled_excluded FOR TESTING. +ENDCLASS. + + +CLASS {{p}}walk_check IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}walker_stats + WITH s_date IN it_date + WITH s_walker IN it_walker + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_walkers. + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( run( ) ) ). + ENDMETHOD. + + METHOD sorted_by_dist. + DATA(lr_data) = run( ). + cl_abap_unit_assert=>assert_equals( exp = `W2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKER` ) ). + cl_abap_unit_assert=>assert_equals( exp = `W3` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `WALKER` ) ). + cl_abap_unit_assert=>assert_equals( exp = `W1` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `WALKER` ) ). + ENDMETHOD. + + METHOD dist_and_walks. + DATA(lr_data) = run( ). + cl_abap_unit_assert=>assert_equals( exp = `10000` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DIST_M` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `6500` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DIST_M` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `WALKS` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( it_date = VALUE #( ( sign = 'I' option = 'BT' low = '20260106' high = '20260110' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `W1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKER` ) ). + cl_abap_unit_assert=>assert_equals( exp = `3500` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DIST_M` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKS` ) ). + ENDMETHOD. + + METHOD walker_filter. + DATA(lr_data) = run( it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'W3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `10000` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DIST_M` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKS` ) ). + ENDMETHOD. + + METHOD cancelled_excluded. + DATA(lr_data) = run( it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'W2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `10000` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DIST_M` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WALKS` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/mutation.json b/tasks_gen/train/G1132/mutation.json new file mode 100644 index 0000000..93ef1b2 --- /dev/null +++ b/tasks_gen/train/G1132/mutation.json @@ -0,0 +1,75 @@ +{ + "task": "G1132", + "mutants": [ + { + "object": "{{P}}WALKER_STATS", + "mutant": "line 29: AND -> OR (logic)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "DIST_AND_WALKS", + "WALKER_FILTER" + ] + }, + { + "object": "{{P}}WALKER_STATS", + "mutant": "line 30: AND -> OR (logic)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "DIST_AND_WALKS", + "WALKER_FILTER" + ] + }, + { + "object": "{{P}}WALKER_STATS", + "mutant": "line 30: = -> <> (eq)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_WALKERS", + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "DIST_AND_WALKS", + "SORTED_BY_DIST", + "WALKER_FILTER" + ] + }, + { + "object": "{{P}}WALKER_STATS", + "mutant": "line 32: >= -> < (rel)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_WALKERS", + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "DIST_AND_WALKS", + "SORTED_BY_DIST", + "WALKER_FILTER" + ] + }, + { + "object": "{{P}}WALKER_STATS", + "mutant": "line 32: 2 -> 3 (const)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_WALKERS", + "CANCELLED_EXCLUDED", + "DATE_FILTER", + "DIST_AND_WALKS", + "SORTED_BY_DIST", + "WALKER_FILTER" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1132/reference/walker_stats.prog.abap b/tasks_gen/train/G1132/reference/walker_stats.prog.abap new file mode 100644 index 0000000..92dc39e --- /dev/null +++ b/tasks_gen/train/G1132/reference/walker_stats.prog.abap @@ -0,0 +1,106 @@ +REPORT {{p}}walker_stats. + +DATA gv_walker TYPE {{p}}walk-walker. +DATA gv_date TYPE {{p}}walk-walk_date. + +SELECT-OPTIONS s_date FOR gv_date. +SELECT-OPTIONS s_walker FOR gv_walker. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES tt_line TYPE STANDARD TABLE OF {{p}}walksum WITH EMPTY KEY. + TYPES tt_walker TYPE RANGE OF {{p}}walk-walker. + TYPES tt_date TYPE RANGE OF {{p}}walk-walk_date. + + METHODS select_totals + IMPORTING it_date TYPE tt_date + it_walker TYPE tt_walker + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT walker, SUM( dist_m ) AS dist_m, COUNT( * ) AS walks + FROM {{p}}walk + WHERE walk_date IN @it_date + AND walker IN @it_walker + AND status = 'F' + GROUP BY walker + HAVING COUNT( * ) >= 2 + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + SORT rt_lines BY dist_m DESCENDING walker ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( it_date = s_date[] it_walker = s_walker[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_walker FOR TESTING. + METHODS excludes_single FOR TESTING. + METHODS ignores_cancelled FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}WALK' ) ) ). + DATA lt_walk TYPE STANDARD TABLE OF {{p}}walk WITH EMPTY KEY. + lt_walk = VALUE #( + ( walk_id = 1 walker = 'A1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'A1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'B2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'B2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'B2' walk_date = '20260108' dist_m = 2000 status = 'F' ) + ( walk_id = 6 walker = 'C3' walk_date = '20260105' dist_m = 9000 status = 'F' ) ). + go_env->insert_test_data( lt_walk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_walker. + DATA(lt_lines) = NEW lcl_report( )->select_totals( it_date = VALUE #( ) it_walker = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 1 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 2 ]-walker ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 5500 ) act = lt_lines[ 2 ]-dist_m ). + ENDMETHOD. + + METHOD excludes_single. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'C3' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD ignores_cancelled. + DATA(lt_lines) = NEW lcl_report( )->select_totals( + it_date = VALUE #( ) + it_walker = VALUE #( ( sign = 'I' option = 'EQ' low = 'B2' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-dist_m( 10000 ) act = lt_lines[ 1 ]-dist_m ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}walksum-walks( 2 ) act = lt_lines[ 1 ]-walks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/seed/walk.tabl.asabap b/tasks_gen/train/G1132/seed/walk.tabl.asabap new file mode 100644 index 0000000..1943fd3 --- /dev/null +++ b/tasks_gen/train/G1132/seed/walk.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Dog walks' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}walk { + key client : abap.clnt not null; + key walk_id : abap.int4 not null; + walker : abap.char(6); + walk_date : abap.dats; + dist_m : abap.int4; + status : abap.char(1); +} diff --git a/tasks_gen/train/G1132/seed/walk_fill.clas.abap b/tasks_gen/train/G1132/seed/walk_fill.clas.abap new file mode 100644 index 0000000..22d1d07 --- /dev/null +++ b/tasks_gen/train/G1132/seed/walk_fill.clas.abap @@ -0,0 +1,21 @@ +CLASS {{p}}walk_fill DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}walk_fill IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}walk. + INSERT {{p}}walk FROM TABLE @( VALUE #( + ( walk_id = 1 walker = 'W1' walk_date = '20260105' dist_m = 3000 status = 'F' ) + ( walk_id = 2 walker = 'W1' walk_date = '20260106' dist_m = 2500 status = 'F' ) + ( walk_id = 3 walker = 'W2' walk_date = '20260105' dist_m = 8000 status = 'F' ) + ( walk_id = 4 walker = 'W2' walk_date = '20260107' dist_m = 1000 status = 'X' ) + ( walk_id = 5 walker = 'W3' walk_date = '20260105' dist_m = 5000 status = 'F' ) + ( walk_id = 6 walker = 'W3' walk_date = '20260106' dist_m = 5000 status = 'F' ) + ( walk_id = 7 walker = 'W4' walk_date = '20260108' dist_m = 9000 status = 'F' ) + ( walk_id = 8 walker = 'W2' walk_date = '20260109' dist_m = 2000 status = 'F' ) + ( walk_id = 9 walker = 'W1' walk_date = '20260110' dist_m = 1000 status = 'F' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1132/seed/walksum.tabl.asabap b/tasks_gen/train/G1132/seed/walksum.tabl.asabap new file mode 100644 index 0000000..1255f33 --- /dev/null +++ b/tasks_gen/train/G1132/seed/walksum.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Output structure of the walk list' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}walksum { + key walker : abap.char(6) not null; + dist_m : abap.int4; + walks : abap.int4; +} diff --git a/tasks_gen/train/G1132/spec.md b/tasks_gen/train/G1132/spec.md new file mode 100644 index 0000000..52c718e --- /dev/null +++ b/tasks_gen/train/G1132/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +A dog walking service wants a summary of its walks. The office uses the list to +see how far each walker went in the period. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}WALK exists in package $TMP and contains data. + Fields: WALK_ID (INT4, key), WALKER (CHAR 6), WALK_DATE (DATS), + DIST_M (INT4, the distance of the walk in metres), STATUS (CHAR 1). +- STATUS has these values: + - 'F': the walk is finished. + - 'X': the walk is cancelled. +- Table {{P}}WALKSUM exists in package $TMP. It is the output structure of the + list. Fields: WALKER (CHAR 6, key), DIST_M (INT4), WALKS (INT4). + +# 4. Contract +- Create the executable program {{P}}WALKER_STATS in package $TMP. +- Selection screen: + - Select-option S_DATE for WALK_DATE: the walk dates to include. + - Select-option S_WALKER for WALKER: the walkers to include. +- Show the result with CL_SALV_TABLE. The output table has the row type of + table {{P}}WALKSUM. The output columns are WALKER, DIST_M (the total distance + in metres) and WALKS (the number of walks). + +# 5. Business rules +1. Include a walk only if WALK_DATE is in S_DATE, WALKER is in S_WALKER and + STATUS is 'F'. +2. An empty S_DATE or an empty S_WALKER does not restrict the result. +3. Show one line for each walker that has at least two included walks. +4. DIST_M is the sum of DIST_M of the included walks of the walker. +5. WALKS is the number of included walks of the walker. +6. Sort the lines by DIST_M, the highest number first. If two lines have the + same DIST_M, sort them by WALKER in ascending order. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Read table {{P}}WALK with one database access. +- Out of scope: do not change {{P}}WALK and {{P}}WALKSUM. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1132/task.json b/tasks_gen/train/G1132/task.json new file mode 100644 index 0000000..d768f33 --- /dev/null +++ b/tasks_gen/train/G1132/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1132", + "category": "B", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}WALK", + "file": "seed/walk.tabl.asabap", + "description": "Walks of the dog walking service" + }, + { + "type": "TABL", + "name": "{{P}}WALKSUM", + "file": "seed/walksum.tabl.asabap", + "description": "Output structure of the walk list" + }, + { + "type": "CLAS", + "name": "{{P}}WALK_FILL", + "file": "seed/walk_fill.clas.abap", + "description": "Walk seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}WALKER_STATS", + "parameters": [ + "s_date", + "s_walker" + ] + } + ], + "out_of_scope": [ + "{{P}}WALK", + "{{P}}WALKSUM" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}WALK_CHECK", + "file": "hidden/walk_check.clas.abap", + "description": "Hidden tests for the report {{P}}WALKER_STATS" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}WALKER_STATS", + "file": "reference/walker_stats.prog.abap", + "description": "Distance per walker" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1133/faulty/m1_taxi_day.prog.abap b/tasks_gen/train/G1133/faulty/m1_taxi_day.prog.abap new file mode 100644 index 0000000..ef14683 --- /dev/null +++ b/tasks_gen/train/G1133/faulty/m1_taxi_day.prog.abap @@ -0,0 +1,245 @@ +REPORT {{p}}taxi_day. + +DATA gv_base TYPE {{p}}driver-base_code. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_base FOR gv_base. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_line, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + avg_fare TYPE ty_amount, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_driver, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + END OF ty_driver, + tt_driver TYPE SORTED TABLE OF ty_driver WITH UNIQUE KEY driver_id. + TYPES: + BEGIN OF ty_trip, + driver_id TYPE {{p}}trip-driver_id, + km TYPE {{p}}trip-km, + fare TYPE {{p}}trip-fare, + END OF ty_trip, + tt_trip TYPE STANDARD TABLE OF ty_trip WITH EMPTY KEY. + TYPES: + BEGIN OF ty_sum, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + END OF ty_sum, + tt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY driver_id. + METHODS read_trips + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_trips) TYPE tt_trip. + METHODS read_drivers + IMPORTING it_trips TYPE tt_trip + RETURNING VALUE(rt_drivers) TYPE tt_driver. + METHODS base_matches + IMPORTING is_driver TYPE ty_driver + it_base TYPE tt_base + RETURNING VALUE(rv_matches) TYPE abap_bool. + METHODS add_trip + IMPORTING is_trip TYPE ty_trip + is_driver TYPE ty_driver + CHANGING ct_sum TYPE tt_sum. + METHODS build_lines + IMPORTING it_sum TYPE tt_sum + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + DATA(lt_trips) = read_trips( iv_from = iv_from iv_to = iv_to ). + IF lt_trips IS INITIAL. + RETURN. + ENDIF. + + DATA(lt_drivers) = read_drivers( lt_trips ). + DATA lt_sum TYPE tt_sum. + + LOOP AT lt_trips INTO DATA(ls_trip). + READ TABLE lt_drivers INTO DATA(ls_driver) + WITH TABLE KEY driver_id = ls_trip-driver_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + IF base_matches( is_driver = ls_driver it_base = it_base ) = abap_false. + CONTINUE. + ENDIF. + add_trip( EXPORTING is_trip = ls_trip + is_driver = ls_driver + CHANGING ct_sum = lt_sum ). + ENDLOOP. + + rt_lines = build_lines( lt_sum ). + ENDMETHOD. + + METHOD read_trips. + SELECT driver_id, km, fare + FROM {{p}}trip + WHERE trip_date >= @iv_from + OR trip_date <= @iv_to + INTO TABLE @rt_trips. + ENDMETHOD. + + METHOD read_drivers. + DATA lt_ids TYPE RANGE OF {{p}}driver-driver_id. + lt_ids = VALUE #( FOR ls_trip IN it_trips + ( sign = 'I' option = 'EQ' low = ls_trip-driver_id ) ). + + SELECT driver_id, driver_name, base_code + FROM {{p}}driver + WHERE driver_id IN @lt_ids + INTO TABLE @rt_drivers. + ENDMETHOD. + + METHOD base_matches. + IF it_base IS INITIAL. + rv_matches = abap_true. + RETURN. + ENDIF. + IF is_driver-base_code IN it_base. + rv_matches = abap_true. + ELSE. + rv_matches = abap_false. + ENDIF. + ENDMETHOD. + + METHOD add_trip. + READ TABLE ct_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY driver_id = is_driver-driver_id. + IF sy-subrc = 0. + -trips = -trips + 1. + -km = -km + is_trip-km. + -fare = -fare + is_trip-fare. + ELSE. + INSERT VALUE #( driver_id = is_driver-driver_id + driver_name = is_driver-driver_name + base_code = is_driver-base_code + trips = 1 + km = is_trip-km + fare = is_trip-fare ) + INTO TABLE ct_sum. + ENDIF. + ENDMETHOD. + + METHOD build_lines. + DATA lv_avg_fare TYPE ty_amount. + LOOP AT it_sum INTO DATA(ls_sum). + lv_avg_fare = ls_sum-fare / ls_sum-trips. + INSERT VALUE #( driver_id = ls_sum-driver_id + driver_name = ls_sum-driver_name + base_code = ls_sum-base_code + trips = ls_sum-trips + km = ls_sum-km + fare = ls_sum-fare + avg_fare = lv_avg_fare ) + INTO TABLE rt_lines. + ENDLOOP. + SORT rt_lines BY fare DESCENDING driver_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from + iv_to = p_to + it_base = s_base[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_driver FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS orphan_trip FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}TRIP' ) ( '{{P}}DRIVER' ) ) ). + + DATA lt_driver TYPE STANDARD TABLE OF {{p}}driver WITH EMPTY KEY. + lt_driver = VALUE #( ( driver_id = 'D1' driver_name = 'Anna' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben' base_code = 'SOUTH' ) ). + go_env->insert_test_data( lt_driver ). + + DATA lt_trip TYPE STANDARD TABLE OF {{p}}trip WITH EMPTY KEY. + lt_trip = VALUE #( ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '10.00' fare = '30.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '5.00' fare = '15.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '8.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D9' trip_date = '20260110' km = '1.00' fare = '5.00' ) ). + go_env->insert_test_data( lt_trip ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_driver. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-trips ). + cl_abap_unit_assert=>assert_equals( + exp = CONV lcl_report=>ty_amount( 45 ) + act = lt_lines[ 1 ]-fare ). + ENDMETHOD. + + METHOD base_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + ENDMETHOD. + + METHOD orphan_trip. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/faulty/m2_taxi_day.prog.abap b/tasks_gen/train/G1133/faulty/m2_taxi_day.prog.abap new file mode 100644 index 0000000..d7df227 --- /dev/null +++ b/tasks_gen/train/G1133/faulty/m2_taxi_day.prog.abap @@ -0,0 +1,245 @@ +REPORT {{p}}taxi_day. + +DATA gv_base TYPE {{p}}driver-base_code. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_base FOR gv_base. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_line, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + avg_fare TYPE ty_amount, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_driver, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + END OF ty_driver, + tt_driver TYPE SORTED TABLE OF ty_driver WITH UNIQUE KEY driver_id. + TYPES: + BEGIN OF ty_trip, + driver_id TYPE {{p}}trip-driver_id, + km TYPE {{p}}trip-km, + fare TYPE {{p}}trip-fare, + END OF ty_trip, + tt_trip TYPE STANDARD TABLE OF ty_trip WITH EMPTY KEY. + TYPES: + BEGIN OF ty_sum, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + END OF ty_sum, + tt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY driver_id. + METHODS read_trips + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_trips) TYPE tt_trip. + METHODS read_drivers + IMPORTING it_trips TYPE tt_trip + RETURNING VALUE(rt_drivers) TYPE tt_driver. + METHODS base_matches + IMPORTING is_driver TYPE ty_driver + it_base TYPE tt_base + RETURNING VALUE(rv_matches) TYPE abap_bool. + METHODS add_trip + IMPORTING is_trip TYPE ty_trip + is_driver TYPE ty_driver + CHANGING ct_sum TYPE tt_sum. + METHODS build_lines + IMPORTING it_sum TYPE tt_sum + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + DATA(lt_trips) = read_trips( iv_from = iv_from iv_to = iv_to ). + IF lt_trips IS INITIAL. + RETURN. + ENDIF. + + DATA(lt_drivers) = read_drivers( lt_trips ). + DATA lt_sum TYPE tt_sum. + + LOOP AT lt_trips INTO DATA(ls_trip). + READ TABLE lt_drivers INTO DATA(ls_driver) + WITH TABLE KEY driver_id = ls_trip-driver_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + IF base_matches( is_driver = ls_driver it_base = it_base ) = abap_false. + CONTINUE. + ENDIF. + add_trip( EXPORTING is_trip = ls_trip + is_driver = ls_driver + CHANGING ct_sum = lt_sum ). + ENDLOOP. + + rt_lines = build_lines( lt_sum ). + ENDMETHOD. + + METHOD read_trips. + SELECT driver_id, km, fare + FROM {{p}}trip + WHERE trip_date >= @iv_from + AND trip_date <= @iv_to + INTO TABLE @rt_trips. + ENDMETHOD. + + METHOD read_drivers. + DATA lt_ids TYPE RANGE OF {{p}}driver-driver_id. + lt_ids = VALUE #( FOR ls_trip IN it_trips + ( sign = 'I' option = 'EQ' low = ls_trip-driver_id ) ). + + SELECT driver_id, driver_name, base_code + FROM {{p}}driver + WHERE driver_id IN @lt_ids + INTO TABLE @rt_drivers. + ENDMETHOD. + + METHOD base_matches. + IF it_base IS INITIAL. + rv_matches = abap_false. + RETURN. + ENDIF. + IF is_driver-base_code IN it_base. + rv_matches = abap_true. + ELSE. + rv_matches = abap_false. + ENDIF. + ENDMETHOD. + + METHOD add_trip. + READ TABLE ct_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY driver_id = is_driver-driver_id. + IF sy-subrc = 0. + -trips = -trips + 1. + -km = -km + is_trip-km. + -fare = -fare + is_trip-fare. + ELSE. + INSERT VALUE #( driver_id = is_driver-driver_id + driver_name = is_driver-driver_name + base_code = is_driver-base_code + trips = 1 + km = is_trip-km + fare = is_trip-fare ) + INTO TABLE ct_sum. + ENDIF. + ENDMETHOD. + + METHOD build_lines. + DATA lv_avg_fare TYPE ty_amount. + LOOP AT it_sum INTO DATA(ls_sum). + lv_avg_fare = ls_sum-fare / ls_sum-trips. + INSERT VALUE #( driver_id = ls_sum-driver_id + driver_name = ls_sum-driver_name + base_code = ls_sum-base_code + trips = ls_sum-trips + km = ls_sum-km + fare = ls_sum-fare + avg_fare = lv_avg_fare ) + INTO TABLE rt_lines. + ENDLOOP. + SORT rt_lines BY fare DESCENDING driver_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from + iv_to = p_to + it_base = s_base[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_driver FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS orphan_trip FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}TRIP' ) ( '{{P}}DRIVER' ) ) ). + + DATA lt_driver TYPE STANDARD TABLE OF {{p}}driver WITH EMPTY KEY. + lt_driver = VALUE #( ( driver_id = 'D1' driver_name = 'Anna' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben' base_code = 'SOUTH' ) ). + go_env->insert_test_data( lt_driver ). + + DATA lt_trip TYPE STANDARD TABLE OF {{p}}trip WITH EMPTY KEY. + lt_trip = VALUE #( ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '10.00' fare = '30.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '5.00' fare = '15.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '8.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D9' trip_date = '20260110' km = '1.00' fare = '5.00' ) ). + go_env->insert_test_data( lt_trip ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_driver. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-trips ). + cl_abap_unit_assert=>assert_equals( + exp = CONV lcl_report=>ty_amount( 45 ) + act = lt_lines[ 1 ]-fare ). + ENDMETHOD. + + METHOD base_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + ENDMETHOD. + + METHOD orphan_trip. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/faulty/m3_taxi_day.prog.abap b/tasks_gen/train/G1133/faulty/m3_taxi_day.prog.abap new file mode 100644 index 0000000..feacf96 --- /dev/null +++ b/tasks_gen/train/G1133/faulty/m3_taxi_day.prog.abap @@ -0,0 +1,245 @@ +REPORT {{p}}taxi_day. + +DATA gv_base TYPE {{p}}driver-base_code. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_base FOR gv_base. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_line, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + avg_fare TYPE ty_amount, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_driver, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + END OF ty_driver, + tt_driver TYPE SORTED TABLE OF ty_driver WITH UNIQUE KEY driver_id. + TYPES: + BEGIN OF ty_trip, + driver_id TYPE {{p}}trip-driver_id, + km TYPE {{p}}trip-km, + fare TYPE {{p}}trip-fare, + END OF ty_trip, + tt_trip TYPE STANDARD TABLE OF ty_trip WITH EMPTY KEY. + TYPES: + BEGIN OF ty_sum, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + END OF ty_sum, + tt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY driver_id. + METHODS read_trips + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_trips) TYPE tt_trip. + METHODS read_drivers + IMPORTING it_trips TYPE tt_trip + RETURNING VALUE(rt_drivers) TYPE tt_driver. + METHODS base_matches + IMPORTING is_driver TYPE ty_driver + it_base TYPE tt_base + RETURNING VALUE(rv_matches) TYPE abap_bool. + METHODS add_trip + IMPORTING is_trip TYPE ty_trip + is_driver TYPE ty_driver + CHANGING ct_sum TYPE tt_sum. + METHODS build_lines + IMPORTING it_sum TYPE tt_sum + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + DATA(lt_trips) = read_trips( iv_from = iv_from iv_to = iv_to ). + IF lt_trips IS INITIAL. + RETURN. + ENDIF. + + DATA(lt_drivers) = read_drivers( lt_trips ). + DATA lt_sum TYPE tt_sum. + + LOOP AT lt_trips INTO DATA(ls_trip). + READ TABLE lt_drivers INTO DATA(ls_driver) + WITH TABLE KEY driver_id = ls_trip-driver_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + IF base_matches( is_driver = ls_driver it_base = it_base ) = abap_false. + CONTINUE. + ENDIF. + add_trip( EXPORTING is_trip = ls_trip + is_driver = ls_driver + CHANGING ct_sum = lt_sum ). + ENDLOOP. + + rt_lines = build_lines( lt_sum ). + ENDMETHOD. + + METHOD read_trips. + SELECT driver_id, km, fare + FROM {{p}}trip + WHERE trip_date >= @iv_from + AND trip_date <= @iv_to + INTO TABLE @rt_trips. + ENDMETHOD. + + METHOD read_drivers. + DATA lt_ids TYPE RANGE OF {{p}}driver-driver_id. + lt_ids = VALUE #( FOR ls_trip IN it_trips + ( sign = 'I' option = 'EQ' low = ls_trip-driver_id ) ). + + SELECT driver_id, driver_name, base_code + FROM {{p}}driver + WHERE driver_id IN @lt_ids + INTO TABLE @rt_drivers. + ENDMETHOD. + + METHOD base_matches. + IF it_base IS INITIAL. + rv_matches = abap_true. + RETURN. + ENDIF. + IF is_driver-base_code IN it_base. + rv_matches = abap_true. + ELSE. + rv_matches = abap_false. + ENDIF. + ENDMETHOD. + + METHOD add_trip. + READ TABLE ct_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY driver_id = is_driver-driver_id. + IF sy-subrc = 0. + -trips = -trips + 1. + -km = -km - is_trip-km. + -fare = -fare + is_trip-fare. + ELSE. + INSERT VALUE #( driver_id = is_driver-driver_id + driver_name = is_driver-driver_name + base_code = is_driver-base_code + trips = 1 + km = is_trip-km + fare = is_trip-fare ) + INTO TABLE ct_sum. + ENDIF. + ENDMETHOD. + + METHOD build_lines. + DATA lv_avg_fare TYPE ty_amount. + LOOP AT it_sum INTO DATA(ls_sum). + lv_avg_fare = ls_sum-fare / ls_sum-trips. + INSERT VALUE #( driver_id = ls_sum-driver_id + driver_name = ls_sum-driver_name + base_code = ls_sum-base_code + trips = ls_sum-trips + km = ls_sum-km + fare = ls_sum-fare + avg_fare = lv_avg_fare ) + INTO TABLE rt_lines. + ENDLOOP. + SORT rt_lines BY fare DESCENDING driver_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from + iv_to = p_to + it_base = s_base[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_driver FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS orphan_trip FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}TRIP' ) ( '{{P}}DRIVER' ) ) ). + + DATA lt_driver TYPE STANDARD TABLE OF {{p}}driver WITH EMPTY KEY. + lt_driver = VALUE #( ( driver_id = 'D1' driver_name = 'Anna' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben' base_code = 'SOUTH' ) ). + go_env->insert_test_data( lt_driver ). + + DATA lt_trip TYPE STANDARD TABLE OF {{p}}trip WITH EMPTY KEY. + lt_trip = VALUE #( ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '10.00' fare = '30.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '5.00' fare = '15.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '8.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D9' trip_date = '20260110' km = '1.00' fare = '5.00' ) ). + go_env->insert_test_data( lt_trip ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_driver. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-trips ). + cl_abap_unit_assert=>assert_equals( + exp = CONV lcl_report=>ty_amount( 45 ) + act = lt_lines[ 1 ]-fare ). + ENDMETHOD. + + METHOD base_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + ENDMETHOD. + + METHOD orphan_trip. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/faulty/m4_taxi_day.prog.abap b/tasks_gen/train/G1133/faulty/m4_taxi_day.prog.abap new file mode 100644 index 0000000..4d0b571 --- /dev/null +++ b/tasks_gen/train/G1133/faulty/m4_taxi_day.prog.abap @@ -0,0 +1,245 @@ +REPORT {{p}}taxi_day. + +DATA gv_base TYPE {{p}}driver-base_code. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_base FOR gv_base. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_line, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + avg_fare TYPE ty_amount, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_driver, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + END OF ty_driver, + tt_driver TYPE SORTED TABLE OF ty_driver WITH UNIQUE KEY driver_id. + TYPES: + BEGIN OF ty_trip, + driver_id TYPE {{p}}trip-driver_id, + km TYPE {{p}}trip-km, + fare TYPE {{p}}trip-fare, + END OF ty_trip, + tt_trip TYPE STANDARD TABLE OF ty_trip WITH EMPTY KEY. + TYPES: + BEGIN OF ty_sum, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + END OF ty_sum, + tt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY driver_id. + METHODS read_trips + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_trips) TYPE tt_trip. + METHODS read_drivers + IMPORTING it_trips TYPE tt_trip + RETURNING VALUE(rt_drivers) TYPE tt_driver. + METHODS base_matches + IMPORTING is_driver TYPE ty_driver + it_base TYPE tt_base + RETURNING VALUE(rv_matches) TYPE abap_bool. + METHODS add_trip + IMPORTING is_trip TYPE ty_trip + is_driver TYPE ty_driver + CHANGING ct_sum TYPE tt_sum. + METHODS build_lines + IMPORTING it_sum TYPE tt_sum + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + DATA(lt_trips) = read_trips( iv_from = iv_from iv_to = iv_to ). + IF lt_trips IS INITIAL. + RETURN. + ENDIF. + + DATA(lt_drivers) = read_drivers( lt_trips ). + DATA lt_sum TYPE tt_sum. + + LOOP AT lt_trips INTO DATA(ls_trip). + READ TABLE lt_drivers INTO DATA(ls_driver) + WITH TABLE KEY driver_id = ls_trip-driver_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + IF base_matches( is_driver = ls_driver it_base = it_base ) = abap_false. + CONTINUE. + ENDIF. + add_trip( EXPORTING is_trip = ls_trip + is_driver = ls_driver + CHANGING ct_sum = lt_sum ). + ENDLOOP. + + rt_lines = build_lines( lt_sum ). + ENDMETHOD. + + METHOD read_trips. + SELECT driver_id, km, fare + FROM {{p}}trip + WHERE trip_date >= @iv_from + AND trip_date <= @iv_to + INTO TABLE @rt_trips. + ENDMETHOD. + + METHOD read_drivers. + DATA lt_ids TYPE RANGE OF {{p}}driver-driver_id. + lt_ids = VALUE #( FOR ls_trip IN it_trips + ( sign = 'I' option = 'EQ' low = ls_trip-driver_id ) ). + + SELECT driver_id, driver_name, base_code + FROM {{p}}driver + WHERE driver_id IN @lt_ids + INTO TABLE @rt_drivers. + ENDMETHOD. + + METHOD base_matches. + IF it_base IS INITIAL. + rv_matches = abap_true. + RETURN. + ENDIF. + IF is_driver-base_code IN it_base. + rv_matches = abap_true. + ELSE. + rv_matches = abap_false. + ENDIF. + ENDMETHOD. + + METHOD add_trip. + READ TABLE ct_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY driver_id = is_driver-driver_id. + IF sy-subrc = 0. + -trips = -trips + 1. + -km = -km + is_trip-km. + -fare = -fare + is_trip-fare. + ELSE. + INSERT VALUE #( driver_id = is_driver-driver_id + driver_name = is_driver-driver_name + base_code = is_driver-base_code + trips = 2 + km = is_trip-km + fare = is_trip-fare ) + INTO TABLE ct_sum. + ENDIF. + ENDMETHOD. + + METHOD build_lines. + DATA lv_avg_fare TYPE ty_amount. + LOOP AT it_sum INTO DATA(ls_sum). + lv_avg_fare = ls_sum-fare / ls_sum-trips. + INSERT VALUE #( driver_id = ls_sum-driver_id + driver_name = ls_sum-driver_name + base_code = ls_sum-base_code + trips = ls_sum-trips + km = ls_sum-km + fare = ls_sum-fare + avg_fare = lv_avg_fare ) + INTO TABLE rt_lines. + ENDLOOP. + SORT rt_lines BY fare DESCENDING driver_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from + iv_to = p_to + it_base = s_base[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_driver FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS orphan_trip FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}TRIP' ) ( '{{P}}DRIVER' ) ) ). + + DATA lt_driver TYPE STANDARD TABLE OF {{p}}driver WITH EMPTY KEY. + lt_driver = VALUE #( ( driver_id = 'D1' driver_name = 'Anna' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben' base_code = 'SOUTH' ) ). + go_env->insert_test_data( lt_driver ). + + DATA lt_trip TYPE STANDARD TABLE OF {{p}}trip WITH EMPTY KEY. + lt_trip = VALUE #( ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '10.00' fare = '30.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '5.00' fare = '15.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '8.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D9' trip_date = '20260110' km = '1.00' fare = '5.00' ) ). + go_env->insert_test_data( lt_trip ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_driver. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-trips ). + cl_abap_unit_assert=>assert_equals( + exp = CONV lcl_report=>ty_amount( 45 ) + act = lt_lines[ 1 ]-fare ). + ENDMETHOD. + + METHOD base_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + ENDMETHOD. + + METHOD orphan_trip. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/generation.json b/tasks_gen/train/G1133/generation.json new file mode 100644 index 0000000..69e0852 --- /dev/null +++ b/tasks_gen/train/G1133/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G1133", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 86.8, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 95.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1133/hidden/taxi_hidden.clas.abap b/tasks_gen/train/G1133/hidden/taxi_hidden.clas.abap new file mode 100644 index 0000000..c447732 --- /dev/null +++ b/tasks_gen/train/G1133/hidden/taxi_hidden.clas.abap @@ -0,0 +1,134 @@ +CLASS {{p}}taxi_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_drivers FOR TESTING. + METHODS sorted_by_fare FOR TESTING. + METHODS trips_and_totals FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS orphan_trip_ignored FOR TESTING. +ENDCLASS. + +CLASS {{p}}taxi_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}taxi_day + WITH p_from = iv_from + WITH p_to = iv_to + WITH s_base IN it_base + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + IF ir_data IS NOT BOUND. + cl_abap_unit_assert=>fail( 'No ALV data was returned' ). + ENDIF. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + IF lines( ) < iv_row. + cl_abap_unit_assert=>fail( |Row { iv_row } is missing| ). + ENDIF. + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } is missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_drivers. + cl_abap_unit_assert=>assert_equals( + exp = 4 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ). + ENDMETHOD. + + METHOD sorted_by_fare. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D4` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D1` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D3` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `DRIVER_ID` ) ). + ENDMETHOD. + + METHOD trips_and_totals. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `Ben Costa` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DRIVER_NAME` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `SOUTH` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BASE_CODE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TRIPS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `23.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `KM` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `100.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `33.33` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `AVG_FARE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `20.50` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `AVG_FARE` ) ). + ENDMETHOD. + + METHOD base_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `41.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D3` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DRIVER_ID` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260112' iv_to = '20260112' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `50.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TRIPS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `D3` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DRIVER_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `20.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARE` ) ). + ENDMETHOD. + + METHOD orphan_trip_ignored. + cl_abap_unit_assert=>assert_equals( + exp = 4 + act = lines_of( run( iv_from = '20260110' iv_to = '20260113' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/mutation.json b/tasks_gen/train/G1133/mutation.json new file mode 100644 index 0000000..a252271 --- /dev/null +++ b/tasks_gen/train/G1133/mutation.json @@ -0,0 +1,60 @@ +{ + "task": "G1133", + "mutants": [ + { + "object": "{{P}}TAXI_DAY", + "mutant": "line 92: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}TAXI_DAY", + "mutant": "line 107: AND -> OR (logic)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "BASE_FILTER", + "DATE_FILTER", + "SORTED_BY_FARE", + "TRIPS_AND_TOTALS" + ] + }, + { + "object": "{{P}}TAXI_DAY", + "mutant": "line 124: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "ALL_DRIVERS", + "DATE_FILTER", + "ORPHAN_TRIP_IGNORED", + "SORTED_BY_FARE", + "TRIPS_AND_TOTALS" + ] + }, + { + "object": "{{P}}TAXI_DAY", + "mutant": "line 139: + -> - (arith)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "TRIPS_AND_TOTALS" + ] + }, + { + "object": "{{P}}TAXI_DAY", + "mutant": "line 145: 1 -> 2 (const)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "DATE_FILTER", + "TRIPS_AND_TOTALS" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1133/reference/taxi_day.prog.abap b/tasks_gen/train/G1133/reference/taxi_day.prog.abap new file mode 100644 index 0000000..0ce66ff --- /dev/null +++ b/tasks_gen/train/G1133/reference/taxi_day.prog.abap @@ -0,0 +1,245 @@ +REPORT {{p}}taxi_day. + +DATA gv_base TYPE {{p}}driver-base_code. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_base FOR gv_base. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_line, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + avg_fare TYPE ty_amount, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_base TYPE RANGE OF {{p}}driver-base_code. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_base TYPE tt_base + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_driver, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + END OF ty_driver, + tt_driver TYPE SORTED TABLE OF ty_driver WITH UNIQUE KEY driver_id. + TYPES: + BEGIN OF ty_trip, + driver_id TYPE {{p}}trip-driver_id, + km TYPE {{p}}trip-km, + fare TYPE {{p}}trip-fare, + END OF ty_trip, + tt_trip TYPE STANDARD TABLE OF ty_trip WITH EMPTY KEY. + TYPES: + BEGIN OF ty_sum, + driver_id TYPE {{p}}driver-driver_id, + driver_name TYPE {{p}}driver-driver_name, + base_code TYPE {{p}}driver-base_code, + trips TYPE i, + km TYPE ty_amount, + fare TYPE ty_amount, + END OF ty_sum, + tt_sum TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY driver_id. + METHODS read_trips + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_trips) TYPE tt_trip. + METHODS read_drivers + IMPORTING it_trips TYPE tt_trip + RETURNING VALUE(rt_drivers) TYPE tt_driver. + METHODS base_matches + IMPORTING is_driver TYPE ty_driver + it_base TYPE tt_base + RETURNING VALUE(rv_matches) TYPE abap_bool. + METHODS add_trip + IMPORTING is_trip TYPE ty_trip + is_driver TYPE ty_driver + CHANGING ct_sum TYPE tt_sum. + METHODS build_lines + IMPORTING it_sum TYPE tt_sum + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + DATA(lt_trips) = read_trips( iv_from = iv_from iv_to = iv_to ). + IF lt_trips IS INITIAL. + RETURN. + ENDIF. + + DATA(lt_drivers) = read_drivers( lt_trips ). + DATA lt_sum TYPE tt_sum. + + LOOP AT lt_trips INTO DATA(ls_trip). + READ TABLE lt_drivers INTO DATA(ls_driver) + WITH TABLE KEY driver_id = ls_trip-driver_id. + IF sy-subrc <> 0. + CONTINUE. + ENDIF. + IF base_matches( is_driver = ls_driver it_base = it_base ) = abap_false. + CONTINUE. + ENDIF. + add_trip( EXPORTING is_trip = ls_trip + is_driver = ls_driver + CHANGING ct_sum = lt_sum ). + ENDLOOP. + + rt_lines = build_lines( lt_sum ). + ENDMETHOD. + + METHOD read_trips. + SELECT driver_id, km, fare + FROM {{p}}trip + WHERE trip_date >= @iv_from + AND trip_date <= @iv_to + INTO TABLE @rt_trips. + ENDMETHOD. + + METHOD read_drivers. + DATA lt_ids TYPE RANGE OF {{p}}driver-driver_id. + lt_ids = VALUE #( FOR ls_trip IN it_trips + ( sign = 'I' option = 'EQ' low = ls_trip-driver_id ) ). + + SELECT driver_id, driver_name, base_code + FROM {{p}}driver + WHERE driver_id IN @lt_ids + INTO TABLE @rt_drivers. + ENDMETHOD. + + METHOD base_matches. + IF it_base IS INITIAL. + rv_matches = abap_true. + RETURN. + ENDIF. + IF is_driver-base_code IN it_base. + rv_matches = abap_true. + ELSE. + rv_matches = abap_false. + ENDIF. + ENDMETHOD. + + METHOD add_trip. + READ TABLE ct_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY driver_id = is_driver-driver_id. + IF sy-subrc = 0. + -trips = -trips + 1. + -km = -km + is_trip-km. + -fare = -fare + is_trip-fare. + ELSE. + INSERT VALUE #( driver_id = is_driver-driver_id + driver_name = is_driver-driver_name + base_code = is_driver-base_code + trips = 1 + km = is_trip-km + fare = is_trip-fare ) + INTO TABLE ct_sum. + ENDIF. + ENDMETHOD. + + METHOD build_lines. + DATA lv_avg_fare TYPE ty_amount. + LOOP AT it_sum INTO DATA(ls_sum). + lv_avg_fare = ls_sum-fare / ls_sum-trips. + INSERT VALUE #( driver_id = ls_sum-driver_id + driver_name = ls_sum-driver_name + base_code = ls_sum-base_code + trips = ls_sum-trips + km = ls_sum-km + fare = ls_sum-fare + avg_fare = lv_avg_fare ) + INTO TABLE rt_lines. + ENDLOOP. + SORT rt_lines BY fare DESCENDING driver_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from + iv_to = p_to + it_base = s_base[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_driver FOR TESTING. + METHODS base_filter FOR TESTING. + METHODS orphan_trip FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}TRIP' ) ( '{{P}}DRIVER' ) ) ). + + DATA lt_driver TYPE STANDARD TABLE OF {{p}}driver WITH EMPTY KEY. + lt_driver = VALUE #( ( driver_id = 'D1' driver_name = 'Anna' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben' base_code = 'SOUTH' ) ). + go_env->insert_test_data( lt_driver ). + + DATA lt_trip TYPE STANDARD TABLE OF {{p}}trip WITH EMPTY KEY. + lt_trip = VALUE #( ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '10.00' fare = '30.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '5.00' fare = '15.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '8.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D9' trip_date = '20260110' km = '1.00' fare = '5.00' ) ). + go_env->insert_test_data( lt_trip ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_driver. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-trips ). + cl_abap_unit_assert=>assert_equals( + exp = CONV lcl_report=>ty_amount( 45 ) + act = lt_lines[ 1 ]-fare ). + ENDMETHOD. + + METHOD base_filter. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ( sign = 'I' option = 'EQ' low = 'NORTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = lt_lines[ 1 ]-driver_id ). + ENDMETHOD. + + METHOD orphan_trip. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' + iv_to = '20260131' + it_base = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/seed/driver.tabl.asabap b/tasks_gen/train/G1133/seed/driver.tabl.asabap new file mode 100644 index 0000000..752c3cf --- /dev/null +++ b/tasks_gen/train/G1133/seed/driver.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Taxi driver' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}driver { + key client : abap.clnt not null; + key driver_id : abap.char(6) not null; + driver_name : abap.char(40); + base_code : abap.char(6); +} diff --git a/tasks_gen/train/G1133/seed/taxi_seed.clas.abap b/tasks_gen/train/G1133/seed/taxi_seed.clas.abap new file mode 100644 index 0000000..1d15cea --- /dev/null +++ b/tasks_gen/train/G1133/seed/taxi_seed.clas.abap @@ -0,0 +1,31 @@ +CLASS {{p}}taxi_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}taxi_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}trip. + DELETE FROM {{p}}driver. + + INSERT {{p}}driver FROM TABLE @( VALUE #( + ( driver_id = 'D1' driver_name = 'Anna Berg' base_code = 'NORTH' ) + ( driver_id = 'D2' driver_name = 'Ben Costa' base_code = 'SOUTH' ) + ( driver_id = 'D3' driver_name = 'Cara Diaz' base_code = 'NORTH' ) + ( driver_id = 'D4' driver_name = 'Dan Egan' base_code = 'EAST' ) ) ). + + INSERT {{p}}trip FROM TABLE @( VALUE #( + ( trip_id = 1 driver_id = 'D1' trip_date = '20260110' km = '12.50' fare = '25.00' ) + ( trip_id = 2 driver_id = 'D1' trip_date = '20260111' km = '8.00' fare = '16.00' ) + ( trip_id = 3 driver_id = 'D2' trip_date = '20260110' km = '20.00' fare = '40.00' ) + ( trip_id = 4 driver_id = 'D3' trip_date = '20260112' km = '5.50' fare = '11.00' ) + ( trip_id = 5 driver_id = 'D3' trip_date = '20260112' km = '4.50' fare = '9.00' ) + ( trip_id = 6 driver_id = 'D4' trip_date = '20260113' km = '30.00' fare = '60.00' ) + ( trip_id = 7 driver_id = 'D1' trip_date = '20251231' km = '10.00' fare = '20.00' ) + ( trip_id = 8 driver_id = 'D9' trip_date = '20260110' km = '7.00' fare = '14.00' ) + ( trip_id = 9 driver_id = 'D2' trip_date = '20260111' km = '1.00' fare = '10.00' ) + ( trip_id = 10 driver_id = 'D2' trip_date = '20260112' km = '2.00' fare = '50.00' ) ) ). + + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1133/seed/trip.tabl.asabap b/tasks_gen/train/G1133/seed/trip.tabl.asabap new file mode 100644 index 0000000..62b245e --- /dev/null +++ b/tasks_gen/train/G1133/seed/trip.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Taxi trip' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}trip { + key client : abap.clnt not null; + key trip_id : abap.int4 not null; + driver_id : abap.char(6); + trip_date : abap.dats; + km : abap.dec(7,2); + fare : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1133/spec.md b/tasks_gen/train/G1133/spec.md new file mode 100644 index 0000000..08b181c --- /dev/null +++ b/tasks_gen/train/G1133/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +Show the work of each taxi driver of a taxi company for a period in an ALV +list. The dispatcher uses the list to compare the drivers. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DRIVER exists in package $TMP and contains data. + Fields: DRIVER_ID (CHAR 6, key), DRIVER_NAME (CHAR 40), BASE_CODE (CHAR 6). +- Table {{P}}TRIP exists in package $TMP and contains data. + Fields: TRIP_ID (INT4, key), DRIVER_ID (CHAR 6), TRIP_DATE (DATS), + KM (DEC 7,2), FARE (DEC 15,2). +- A trip belongs to the driver that has the same DRIVER_ID. + +# 4. Contract +- Create the executable program {{P}}TAXI_DAY in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first trip date to include. + - Parameter P_TO, type DATS, mandatory: the last trip date to include. + - Select-option S_BASE for the base code of the driver. +- Show the result with CL_SALV_TABLE. The output table has these columns, with +these names: DRIVER_ID, DRIVER_NAME, BASE_CODE, TRIPS, KM, FARE, AVG_FARE. + +# 5. Business rules +1. Include a trip only if TRIP_DATE is on or after P_FROM and on or before + P_TO. +2. Include a trip only if the base code of its driver is in S_BASE. An empty + S_BASE includes every base code. +3. Ignore a trip if its DRIVER_ID is not in table {{P}}DRIVER. +4. Show one line for each driver that has at least one included trip. +5. DRIVER_ID is the identifier of the driver and DRIVER_NAME is the name of + the driver. BASE_CODE is the base code of the driver. +6. TRIPS is the number of included trips of the driver. +7. KM is the sum of the KM of the included trips of the driver. +8. FARE is the sum of the FARE of the included trips of the driver. +9. AVG_FARE is FARE divided by TRIPS, rounded to 2 decimal places. +10. Sort the lines by FARE, the highest fare first. If two drivers have the + same fare, sort them by DRIVER_ID in ascending order. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Read {{P}}TRIP with one database access and + {{P}}DRIVER with one database access. +- Out of scope: do not change {{P}}DRIVER and {{P}}TRIP. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1133/task.json b/tasks_gen/train/G1133/task.json new file mode 100644 index 0000000..215ed5d --- /dev/null +++ b/tasks_gen/train/G1133/task.json @@ -0,0 +1,69 @@ +{ + "id": "G1133", + "category": "C", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DRIVER", + "file": "seed/driver.tabl.asabap", + "description": "Taxi drivers" + }, + { + "type": "TABL", + "name": "{{P}}TRIP", + "file": "seed/trip.tabl.asabap", + "description": "Taxi trips" + }, + { + "type": "CLAS", + "name": "{{P}}TAXI_SEED", + "file": "seed/taxi_seed.clas.abap", + "description": "Taxi seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}TAXI_DAY", + "parameters": [ + "p_from", + "p_to", + "s_base" + ] + } + ], + "out_of_scope": [ + "{{P}}DRIVER", + "{{P}}TRIP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TAXI_HIDDEN", + "file": "hidden/taxi_hidden.clas.abap", + "description": "Hidden tests for {{P}}TAXI_DAY" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}TAXI_DAY", + "file": "reference/taxi_day.prog.abap", + "description": "Driver summary for a period" + } + ], + "craft_checks": [ + "internal_table_key", + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1135/faulty/m0_pool_ticket.clas.abap b/tasks_gen/train/G1135/faulty/m0_pool_ticket.clas.abap new file mode 100644 index 0000000..d58585d --- /dev/null +++ b/tasks_gen/train/G1135/faulty/m0_pool_ticket.clas.abap @@ -0,0 +1,145 @@ +CLASS {{p}}pool_ticket DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_zone TYPE c LENGTH 2. + TYPES ty_rate TYPE c LENGTH 1. + TYPES ty_day TYPE c LENGTH 2. + TYPES ty_flag TYPE c LENGTH 1. + TYPES ty_status TYPE c LENGTH 2. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + zone TYPE ty_status VALUE 'ZN', + rate TYPE ty_status VALUE 'RT', + day TYPE ty_status VALUE 'DY', + END OF c_status. + + CONSTANTS: + BEGIN OF c_flag, + member TYPE ty_flag VALUE 'X', + guest TYPE ty_flag VALUE ' ', + END OF c_flag. + + METHODS check_booking + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + RETURNING VALUE(rv_status) TYPE ty_status. + + METHODS ticket_price + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + iv_member TYPE ty_flag + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_base_price, + zone TYPE ty_zone, + rate TYPE ty_rate, + base TYPE ty_amount, + END OF ty_base_price, + tt_base_price TYPE HASHED TABLE OF ty_base_price WITH UNIQUE KEY zone rate. + + CONSTANTS c_weekend_surcharge TYPE ty_amount VALUE '2.50'. + CONSTANTS c_member_share TYPE i VALUE 80. + + METHODS base_prices + RETURNING VALUE(rt_price) TYPE tt_base_price. + + METHODS weekend_surcharge + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + + METHODS is_known_zone + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_rate + IMPORTING iv_rate TYPE ty_rate + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_day + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pool_ticket IMPLEMENTATION. + METHOD check_booking. + IF is_known_zone( iv_zone ) = abap_false. + rv_status = c_status-zone. + RETURN. + ENDIF. + IF is_known_rate( iv_rate ) = abap_false. + rv_status = c_status-rate. + RETURN. + ENDIF. + IF is_known_day( iv_day ) = abap_true. + rv_status = c_status-day. + RETURN. + ENDIF. + rv_status = c_status-ok. + ENDMETHOD. + + METHOD ticket_price. + IF check_booking( iv_zone = iv_zone + iv_rate = iv_rate + iv_day = iv_day ) <> c_status-ok. + rv_price = 0. + RETURN. + ENDIF. + + DATA(lt_price) = base_prices( ). + READ TABLE lt_price INTO DATA(ls_price) + WITH TABLE KEY zone = iv_zone rate = iv_rate. + IF sy-subrc <> 0. + rv_price = 0. + RETURN. + ENDIF. + + rv_price = ls_price-base + weekend_surcharge( iv_day ). + IF iv_member = c_flag-member. + rv_price = rv_price * c_member_share / 100. + ENDIF. + ENDMETHOD. + + METHOD base_prices. + rt_price = VALUE #( + ( zone = 'IN' rate = 'A' base = '10.00' ) + ( zone = 'IN' rate = 'C' base = '5.00' ) + ( zone = 'IN' rate = 'S' base = '7.50' ) + ( zone = 'OU' rate = 'A' base = '7.50' ) + ( zone = 'OU' rate = 'C' base = '3.75' ) + ( zone = 'OU' rate = 'S' base = '5.00' ) + ( zone = 'SA' rate = 'A' base = '15.00' ) + ( zone = 'SA' rate = 'C' base = '7.50' ) + ( zone = 'SA' rate = 'S' base = '11.25' ) + ( zone = 'AL' rate = 'A' base = '20.00' ) + ( zone = 'AL' rate = 'C' base = '10.00' ) + ( zone = 'AL' rate = 'S' base = '15.00' ) ). + ENDMETHOD. + + METHOD weekend_surcharge. + IF iv_day = 'SA' OR iv_day = 'SU'. + rv_surcharge = c_weekend_surcharge. + ENDIF. + ENDMETHOD. + + METHOD is_known_zone. + rv_known = xsdbool( iv_zone = 'IN' OR iv_zone = 'OU' + OR iv_zone = 'SA' OR iv_zone = 'AL' ). + ENDMETHOD. + + METHOD is_known_rate. + rv_known = xsdbool( iv_rate = 'A' OR iv_rate = 'C' OR iv_rate = 'S' ). + ENDMETHOD. + + METHOD is_known_day. + rv_known = xsdbool( iv_day = 'MO' OR iv_day = 'TU' OR iv_day = 'WE' + OR iv_day = 'TH' OR iv_day = 'FR' OR iv_day = 'SA' + OR iv_day = 'SU' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/faulty/m2_pool_ticket.clas.abap b/tasks_gen/train/G1135/faulty/m2_pool_ticket.clas.abap new file mode 100644 index 0000000..be7f6e2 --- /dev/null +++ b/tasks_gen/train/G1135/faulty/m2_pool_ticket.clas.abap @@ -0,0 +1,145 @@ +CLASS {{p}}pool_ticket DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_zone TYPE c LENGTH 2. + TYPES ty_rate TYPE c LENGTH 1. + TYPES ty_day TYPE c LENGTH 2. + TYPES ty_flag TYPE c LENGTH 1. + TYPES ty_status TYPE c LENGTH 2. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + zone TYPE ty_status VALUE 'ZN', + rate TYPE ty_status VALUE 'RT', + day TYPE ty_status VALUE 'DY', + END OF c_status. + + CONSTANTS: + BEGIN OF c_flag, + member TYPE ty_flag VALUE 'X', + guest TYPE ty_flag VALUE ' ', + END OF c_flag. + + METHODS check_booking + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + RETURNING VALUE(rv_status) TYPE ty_status. + + METHODS ticket_price + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + iv_member TYPE ty_flag + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_base_price, + zone TYPE ty_zone, + rate TYPE ty_rate, + base TYPE ty_amount, + END OF ty_base_price, + tt_base_price TYPE HASHED TABLE OF ty_base_price WITH UNIQUE KEY zone rate. + + CONSTANTS c_weekend_surcharge TYPE ty_amount VALUE '2.50'. + CONSTANTS c_member_share TYPE i VALUE 80. + + METHODS base_prices + RETURNING VALUE(rt_price) TYPE tt_base_price. + + METHODS weekend_surcharge + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + + METHODS is_known_zone + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_rate + IMPORTING iv_rate TYPE ty_rate + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_day + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pool_ticket IMPLEMENTATION. + METHOD check_booking. + IF is_known_zone( iv_zone ) = abap_false. + rv_status = c_status-zone. + RETURN. + ENDIF. + IF is_known_rate( iv_rate ) = abap_false. + rv_status = c_status-rate. + RETURN. + ENDIF. + IF is_known_day( iv_day ) = abap_false. + rv_status = c_status-day. + RETURN. + ENDIF. + rv_status = c_status-ok. + ENDMETHOD. + + METHOD ticket_price. + IF check_booking( iv_zone = iv_zone + iv_rate = iv_rate + iv_day = iv_day ) = c_status-ok. + rv_price = 0. + RETURN. + ENDIF. + + DATA(lt_price) = base_prices( ). + READ TABLE lt_price INTO DATA(ls_price) + WITH TABLE KEY zone = iv_zone rate = iv_rate. + IF sy-subrc <> 0. + rv_price = 0. + RETURN. + ENDIF. + + rv_price = ls_price-base + weekend_surcharge( iv_day ). + IF iv_member = c_flag-member. + rv_price = rv_price * c_member_share / 100. + ENDIF. + ENDMETHOD. + + METHOD base_prices. + rt_price = VALUE #( + ( zone = 'IN' rate = 'A' base = '10.00' ) + ( zone = 'IN' rate = 'C' base = '5.00' ) + ( zone = 'IN' rate = 'S' base = '7.50' ) + ( zone = 'OU' rate = 'A' base = '7.50' ) + ( zone = 'OU' rate = 'C' base = '3.75' ) + ( zone = 'OU' rate = 'S' base = '5.00' ) + ( zone = 'SA' rate = 'A' base = '15.00' ) + ( zone = 'SA' rate = 'C' base = '7.50' ) + ( zone = 'SA' rate = 'S' base = '11.25' ) + ( zone = 'AL' rate = 'A' base = '20.00' ) + ( zone = 'AL' rate = 'C' base = '10.00' ) + ( zone = 'AL' rate = 'S' base = '15.00' ) ). + ENDMETHOD. + + METHOD weekend_surcharge. + IF iv_day = 'SA' OR iv_day = 'SU'. + rv_surcharge = c_weekend_surcharge. + ENDIF. + ENDMETHOD. + + METHOD is_known_zone. + rv_known = xsdbool( iv_zone = 'IN' OR iv_zone = 'OU' + OR iv_zone = 'SA' OR iv_zone = 'AL' ). + ENDMETHOD. + + METHOD is_known_rate. + rv_known = xsdbool( iv_rate = 'A' OR iv_rate = 'C' OR iv_rate = 'S' ). + ENDMETHOD. + + METHOD is_known_day. + rv_known = xsdbool( iv_day = 'MO' OR iv_day = 'TU' OR iv_day = 'WE' + OR iv_day = 'TH' OR iv_day = 'FR' OR iv_day = 'SA' + OR iv_day = 'SU' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/faulty/m3_pool_ticket.clas.abap b/tasks_gen/train/G1135/faulty/m3_pool_ticket.clas.abap new file mode 100644 index 0000000..159003b --- /dev/null +++ b/tasks_gen/train/G1135/faulty/m3_pool_ticket.clas.abap @@ -0,0 +1,145 @@ +CLASS {{p}}pool_ticket DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_zone TYPE c LENGTH 2. + TYPES ty_rate TYPE c LENGTH 1. + TYPES ty_day TYPE c LENGTH 2. + TYPES ty_flag TYPE c LENGTH 1. + TYPES ty_status TYPE c LENGTH 2. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + zone TYPE ty_status VALUE 'ZN', + rate TYPE ty_status VALUE 'RT', + day TYPE ty_status VALUE 'DY', + END OF c_status. + + CONSTANTS: + BEGIN OF c_flag, + member TYPE ty_flag VALUE 'X', + guest TYPE ty_flag VALUE ' ', + END OF c_flag. + + METHODS check_booking + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + RETURNING VALUE(rv_status) TYPE ty_status. + + METHODS ticket_price + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + iv_member TYPE ty_flag + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_base_price, + zone TYPE ty_zone, + rate TYPE ty_rate, + base TYPE ty_amount, + END OF ty_base_price, + tt_base_price TYPE HASHED TABLE OF ty_base_price WITH UNIQUE KEY zone rate. + + CONSTANTS c_weekend_surcharge TYPE ty_amount VALUE '2.50'. + CONSTANTS c_member_share TYPE i VALUE 80. + + METHODS base_prices + RETURNING VALUE(rt_price) TYPE tt_base_price. + + METHODS weekend_surcharge + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + + METHODS is_known_zone + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_rate + IMPORTING iv_rate TYPE ty_rate + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_day + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pool_ticket IMPLEMENTATION. + METHOD check_booking. + IF is_known_zone( iv_zone ) = abap_false. + rv_status = c_status-zone. + RETURN. + ENDIF. + IF is_known_rate( iv_rate ) = abap_false. + rv_status = c_status-rate. + RETURN. + ENDIF. + IF is_known_day( iv_day ) = abap_false. + rv_status = c_status-day. + RETURN. + ENDIF. + rv_status = c_status-ok. + ENDMETHOD. + + METHOD ticket_price. + IF check_booking( iv_zone = iv_zone + iv_rate = iv_rate + iv_day = iv_day ) <> c_status-ok. + rv_price = 0. + RETURN. + ENDIF. + + DATA(lt_price) = base_prices( ). + READ TABLE lt_price INTO DATA(ls_price) + WITH TABLE KEY zone = iv_zone rate = iv_rate. + IF sy-subrc <> 0. + rv_price = 0. + RETURN. + ENDIF. + + rv_price = ls_price-base - weekend_surcharge( iv_day ). + IF iv_member = c_flag-member. + rv_price = rv_price * c_member_share / 100. + ENDIF. + ENDMETHOD. + + METHOD base_prices. + rt_price = VALUE #( + ( zone = 'IN' rate = 'A' base = '10.00' ) + ( zone = 'IN' rate = 'C' base = '5.00' ) + ( zone = 'IN' rate = 'S' base = '7.50' ) + ( zone = 'OU' rate = 'A' base = '7.50' ) + ( zone = 'OU' rate = 'C' base = '3.75' ) + ( zone = 'OU' rate = 'S' base = '5.00' ) + ( zone = 'SA' rate = 'A' base = '15.00' ) + ( zone = 'SA' rate = 'C' base = '7.50' ) + ( zone = 'SA' rate = 'S' base = '11.25' ) + ( zone = 'AL' rate = 'A' base = '20.00' ) + ( zone = 'AL' rate = 'C' base = '10.00' ) + ( zone = 'AL' rate = 'S' base = '15.00' ) ). + ENDMETHOD. + + METHOD weekend_surcharge. + IF iv_day = 'SA' OR iv_day = 'SU'. + rv_surcharge = c_weekend_surcharge. + ENDIF. + ENDMETHOD. + + METHOD is_known_zone. + rv_known = xsdbool( iv_zone = 'IN' OR iv_zone = 'OU' + OR iv_zone = 'SA' OR iv_zone = 'AL' ). + ENDMETHOD. + + METHOD is_known_rate. + rv_known = xsdbool( iv_rate = 'A' OR iv_rate = 'C' OR iv_rate = 'S' ). + ENDMETHOD. + + METHOD is_known_day. + rv_known = xsdbool( iv_day = 'MO' OR iv_day = 'TU' OR iv_day = 'WE' + OR iv_day = 'TH' OR iv_day = 'FR' OR iv_day = 'SA' + OR iv_day = 'SU' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/generation.json b/tasks_gen/train/G1135/generation.json new file mode 100644 index 0000000..8d2b8e5 --- /dev/null +++ b/tasks_gen/train/G1135/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1135", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1135/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1135/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..d922c33 --- /dev/null +++ b/tasks_gen/train/G1135/hidden/t31_hidden.clas.abap @@ -0,0 +1,113 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pool_ticket. + DATA mv_member TYPE {{p}}pool_ticket=>ty_flag. + DATA mv_guest TYPE {{p}}pool_ticket=>ty_flag. + + METHODS setup. + METHODS valid_booking_is_ok FOR TESTING. + METHODS unknown_zone FOR TESTING. + METHODS unknown_rate FOR TESTING. + METHODS unknown_day FOR TESTING. + METHODS zone_checked_first FOR TESTING. + METHODS lower_case_zone FOR TESTING. + METHODS price_indoor_adult FOR TESTING. + METHODS price_outdoor_child_weekend FOR TESTING. + METHODS price_sauna_senior_member FOR TESTING. + METHODS price_all_areas_weekend_member FOR TESTING. + METHODS price_member_weekday_child FOR TESTING. + METHODS price_invalid_booking FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}pool_ticket( ). + mv_member = {{p}}pool_ticket=>c_flag-member. + mv_guest = {{p}}pool_ticket=>c_flag-guest. + ENDMETHOD. + + METHOD valid_booking_is_ok. + DATA lv_status TYPE {{p}}pool_ticket=>ty_status. + lv_status = mo_cut->check_booking( iv_zone = 'IN' iv_rate = 'A' iv_day = 'MO' ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}pool_ticket=>c_status-ok + act = lv_status ). + ENDMETHOD. + + METHOD unknown_zone. + DATA(lv_zone) = CONV {{p}}pool_ticket=>ty_zone( 'XX' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-zone + act = mo_cut->check_booking( iv_zone = lv_zone iv_rate = 'A' iv_day = 'MO' ) ). + ENDMETHOD. + + METHOD unknown_rate. + DATA(lv_rate) = CONV {{p}}pool_ticket=>ty_rate( 'X' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-rate + act = mo_cut->check_booking( iv_zone = 'IN' iv_rate = lv_rate iv_day = 'XX' ) ). + ENDMETHOD. + + METHOD unknown_day. + DATA(lv_day) = CONV {{p}}pool_ticket=>ty_day( 'XX' ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-day + act = mo_cut->check_booking( iv_zone = 'IN' iv_rate = 'A' iv_day = lv_day ) ). + ENDMETHOD. + + METHOD zone_checked_first. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-zone + act = mo_cut->check_booking( iv_zone = 'XX' iv_rate = 'X' iv_day = 'XX' ) ). + ENDMETHOD. + + METHOD lower_case_zone. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-zone + act = mo_cut->check_booking( iv_zone = 'in' iv_rate = 'A' iv_day = 'MO' ) ). + ENDMETHOD. + + METHOD price_indoor_adult. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '10.00' ) + act = mo_cut->ticket_price( iv_zone = 'IN' iv_rate = 'A' iv_day = 'MO' + iv_member = mv_guest ) ). + ENDMETHOD. + + METHOD price_outdoor_child_weekend. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '6.25' ) + act = mo_cut->ticket_price( iv_zone = 'OU' iv_rate = 'C' iv_day = 'SA' + iv_member = mv_guest ) ). + ENDMETHOD. + + METHOD price_sauna_senior_member. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '9.00' ) + act = mo_cut->ticket_price( iv_zone = 'SA' iv_rate = 'S' iv_day = 'MO' + iv_member = mv_member ) ). + ENDMETHOD. + + METHOD price_all_areas_weekend_member. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '18.00' ) + act = mo_cut->ticket_price( iv_zone = 'AL' iv_rate = 'A' iv_day = 'SU' + iv_member = mv_member ) ). + ENDMETHOD. + + METHOD price_member_weekday_child. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '4.00' ) + act = mo_cut->ticket_price( iv_zone = 'IN' iv_rate = 'C' iv_day = 'WE' + iv_member = mv_member ) ). + ENDMETHOD. + + METHOD price_invalid_booking. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '0.00' ) + act = mo_cut->ticket_price( iv_zone = 'XX' iv_rate = 'A' iv_day = 'MO' + iv_member = mv_guest ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/mutation.json b/tasks_gen/train/G1135/mutation.json new file mode 100644 index 0000000..c2b17ff --- /dev/null +++ b/tasks_gen/train/G1135/mutation.json @@ -0,0 +1,61 @@ +{ + "task": "G1135", + "mutants": [ + { + "object": "{{P}}POOL_TICKET", + "mutant": "line 80: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "PRICE_ALL_AREAS_WEEKEND_MEMBER", + "PRICE_INDOOR_ADULT", + "PRICE_MEMBER_WEEKDAY_CHILD", + "PRICE_OUTDOOR_CHILD_WEEKEND", + "PRICE_SAUNA_SENIOR_MEMBER", + "UNKNOWN_DAY", + "VALID_BOOKING_IS_OK" + ] + }, + { + "object": "{{P}}POOL_TICKET", + "mutant": "line 88: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}POOL_TICKET", + "mutant": "line 90: <> -> = (rel)", + "status": "killed", + "hidden": "7/12", + "failed_tests": [ + "PRICE_ALL_AREAS_WEEKEND_MEMBER", + "PRICE_INDOOR_ADULT", + "PRICE_MEMBER_WEEKDAY_CHILD", + "PRICE_OUTDOOR_CHILD_WEEKEND", + "PRICE_SAUNA_SENIOR_MEMBER" + ] + }, + { + "object": "{{P}}POOL_TICKET", + "mutant": "line 103: + -> - (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "PRICE_ALL_AREAS_WEEKEND_MEMBER", + "PRICE_OUTDOOR_CHILD_WEEKEND" + ] + }, + { + "object": "{{P}}POOL_TICKET", + "mutant": "line 142: OR -> AND (logic)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + } + ], + "valid": 4, + "killed": 3, + "kill_rate": 0.75, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1135/reference/pool_ticket.clas.abap b/tasks_gen/train/G1135/reference/pool_ticket.clas.abap new file mode 100644 index 0000000..83d7fbe --- /dev/null +++ b/tasks_gen/train/G1135/reference/pool_ticket.clas.abap @@ -0,0 +1,145 @@ +CLASS {{p}}pool_ticket DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_zone TYPE c LENGTH 2. + TYPES ty_rate TYPE c LENGTH 1. + TYPES ty_day TYPE c LENGTH 2. + TYPES ty_flag TYPE c LENGTH 1. + TYPES ty_status TYPE c LENGTH 2. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + zone TYPE ty_status VALUE 'ZN', + rate TYPE ty_status VALUE 'RT', + day TYPE ty_status VALUE 'DY', + END OF c_status. + + CONSTANTS: + BEGIN OF c_flag, + member TYPE ty_flag VALUE 'X', + guest TYPE ty_flag VALUE ' ', + END OF c_flag. + + METHODS check_booking + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + RETURNING VALUE(rv_status) TYPE ty_status. + + METHODS ticket_price + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + iv_member TYPE ty_flag + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_base_price, + zone TYPE ty_zone, + rate TYPE ty_rate, + base TYPE ty_amount, + END OF ty_base_price, + tt_base_price TYPE HASHED TABLE OF ty_base_price WITH UNIQUE KEY zone rate. + + CONSTANTS c_weekend_surcharge TYPE ty_amount VALUE '2.50'. + CONSTANTS c_member_share TYPE i VALUE 80. + + METHODS base_prices + RETURNING VALUE(rt_price) TYPE tt_base_price. + + METHODS weekend_surcharge + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + + METHODS is_known_zone + IMPORTING iv_zone TYPE ty_zone + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_rate + IMPORTING iv_rate TYPE ty_rate + RETURNING VALUE(rv_known) TYPE abap_bool. + + METHODS is_known_day + IMPORTING iv_day TYPE ty_day + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}pool_ticket IMPLEMENTATION. + METHOD check_booking. + IF is_known_zone( iv_zone ) = abap_false. + rv_status = c_status-zone. + RETURN. + ENDIF. + IF is_known_rate( iv_rate ) = abap_false. + rv_status = c_status-rate. + RETURN. + ENDIF. + IF is_known_day( iv_day ) = abap_false. + rv_status = c_status-day. + RETURN. + ENDIF. + rv_status = c_status-ok. + ENDMETHOD. + + METHOD ticket_price. + IF check_booking( iv_zone = iv_zone + iv_rate = iv_rate + iv_day = iv_day ) <> c_status-ok. + rv_price = 0. + RETURN. + ENDIF. + + DATA(lt_price) = base_prices( ). + READ TABLE lt_price INTO DATA(ls_price) + WITH TABLE KEY zone = iv_zone rate = iv_rate. + IF sy-subrc <> 0. + rv_price = 0. + RETURN. + ENDIF. + + rv_price = ls_price-base + weekend_surcharge( iv_day ). + IF iv_member = c_flag-member. + rv_price = rv_price * c_member_share / 100. + ENDIF. + ENDMETHOD. + + METHOD base_prices. + rt_price = VALUE #( + ( zone = 'IN' rate = 'A' base = '10.00' ) + ( zone = 'IN' rate = 'C' base = '5.00' ) + ( zone = 'IN' rate = 'S' base = '7.50' ) + ( zone = 'OU' rate = 'A' base = '7.50' ) + ( zone = 'OU' rate = 'C' base = '3.75' ) + ( zone = 'OU' rate = 'S' base = '5.00' ) + ( zone = 'SA' rate = 'A' base = '15.00' ) + ( zone = 'SA' rate = 'C' base = '7.50' ) + ( zone = 'SA' rate = 'S' base = '11.25' ) + ( zone = 'AL' rate = 'A' base = '20.00' ) + ( zone = 'AL' rate = 'C' base = '10.00' ) + ( zone = 'AL' rate = 'S' base = '15.00' ) ). + ENDMETHOD. + + METHOD weekend_surcharge. + IF iv_day = 'SA' OR iv_day = 'SU'. + rv_surcharge = c_weekend_surcharge. + ENDIF. + ENDMETHOD. + + METHOD is_known_zone. + rv_known = xsdbool( iv_zone = 'IN' OR iv_zone = 'OU' + OR iv_zone = 'SA' OR iv_zone = 'AL' ). + ENDMETHOD. + + METHOD is_known_rate. + rv_known = xsdbool( iv_rate = 'A' OR iv_rate = 'C' OR iv_rate = 'S' ). + ENDMETHOD. + + METHOD is_known_day. + rv_known = xsdbool( iv_day = 'MO' OR iv_day = 'TU' OR iv_day = 'WE' + OR iv_day = 'TH' OR iv_day = 'FR' OR iv_day = 'SA' + OR iv_day = 'SU' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/reference/pool_ticket.testclasses.abap b/tasks_gen/train/G1135/reference/pool_ticket.testclasses.abap new file mode 100644 index 0000000..411c0c9 --- /dev/null +++ b/tasks_gen/train/G1135/reference/pool_ticket.testclasses.abap @@ -0,0 +1,75 @@ +CLASS ltc_pool_ticket DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}pool_ticket. + + METHODS setup. + METHODS valid_booking FOR TESTING. + METHODS unknown_zone FOR TESTING. + METHODS unknown_rate FOR TESTING. + METHODS unknown_day FOR TESTING. + METHODS price_indoor_adult FOR TESTING. + METHODS price_weekend_surcharge FOR TESTING. + METHODS price_member_discount FOR TESTING. + METHODS price_invalid_booking FOR TESTING. +ENDCLASS. + + +CLASS ltc_pool_ticket IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}pool_ticket( ). + ENDMETHOD. + + METHOD valid_booking. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-ok + act = mo_cut->check_booking( iv_zone = 'AL' iv_rate = 'S' iv_day = 'SU' ) ). + ENDMETHOD. + + METHOD unknown_zone. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-zone + act = mo_cut->check_booking( iv_zone = 'ZZ' iv_rate = 'A' iv_day = 'MO' ) ). + ENDMETHOD. + + METHOD unknown_rate. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-rate + act = mo_cut->check_booking( iv_zone = 'OU' iv_rate = 'B' iv_day = 'MO' ) ). + ENDMETHOD. + + METHOD unknown_day. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}pool_ticket=>c_status-day + act = mo_cut->check_booking( iv_zone = 'OU' iv_rate = 'C' iv_day = 'XX' ) ). + ENDMETHOD. + + METHOD price_indoor_adult. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '10.00' ) + act = mo_cut->ticket_price( iv_zone = 'IN' iv_rate = 'A' iv_day = 'TU' + iv_member = {{p}}pool_ticket=>c_flag-guest ) ). + ENDMETHOD. + + METHOD price_weekend_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '12.50' ) + act = mo_cut->ticket_price( iv_zone = 'IN' iv_rate = 'A' iv_day = 'SU' + iv_member = {{p}}pool_ticket=>c_flag-guest ) ). + ENDMETHOD. + + METHOD price_member_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '6.00' ) + act = mo_cut->ticket_price( iv_zone = 'OU' iv_rate = 'A' iv_day = 'FR' + iv_member = {{p}}pool_ticket=>c_flag-member ) ). + ENDMETHOD. + + METHOD price_invalid_booking. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}pool_ticket=>ty_amount( '0.00' ) + act = mo_cut->ticket_price( iv_zone = 'IN' iv_rate = 'A' iv_day = 'XX' + iv_member = {{p}}pool_ticket=>c_flag-member ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1135/spec.md b/tasks_gen/train/G1135/spec.md new file mode 100644 index 0000000..15b071c --- /dev/null +++ b/tasks_gen/train/G1135/spec.md @@ -0,0 +1,70 @@ +# 1. Goal +A leisure centre prices the entry tickets of its swimming pool. The ticket +desk calls the class for each visitor and prints the price. The desk also needs +one status code for each booking request. + +# 2. Open questions +None. + +# 3. Context +- No object of this task exists yet. Create everything that the contract lists. +- The leisure centre has four areas. Their area codes are IN (indoor pool), + OU (outdoor pool), SA (sauna area) and AL (all areas). +- The visitor rates are A (adult), C (child) and S (senior). +- The day keys are MO, TU, WE, TH, FR, SA and SU. + +# 4. Contract +- Create the class {{P}}POOL_TICKET in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + ty_zone TYPE c LENGTH 2, ty_rate TYPE c LENGTH 1, ty_day TYPE c LENGTH 2, + ty_flag TYPE c LENGTH 1, ty_status TYPE c LENGTH 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2. +- The class has the public constant structure c_status with the components + ok = 'OK', zone = 'ZN', rate = 'RT', day = 'DY'. +- The class has the public constant structure c_flag with the components + member = 'X', guest = ' '. +- The class has these public methods: + check_booking + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + RETURNING VALUE(rv_status) TYPE ty_status. + ticket_price + IMPORTING iv_zone TYPE ty_zone + iv_rate TYPE ty_rate + iv_day TYPE ty_day + iv_member TYPE ty_flag + RETURNING VALUE(rv_price) TYPE ty_amount. +- Do not add other public methods. + +# 5. Business rules +1. An area code is known if it is IN, OU, SA or AL. Compare the codes exactly. + Do not convert the codes to upper case. +2. A rate code is known if it is A, C or S. +3. A day key is known if it is MO, TU, WE, TH, FR, SA or SU. +4. check_booking checks the area code, then the rate code, then the day key. + It returns the status of the first check that fails. If all three checks + pass, it returns OK. +5. ticket_price returns 0 if check_booking does not return OK. +6. Else the price is the base price of the area and the rate, plus the weekend + surcharge, minus the member discount. +7. The base price in euro: + IN: A 10.00, C 5.00, S 7.50 + OU: A 7.50, C 3.75, S 5.00 + SA: A 15.00, C 7.50, S 11.25 + AL: A 20.00, C 10.00, S 15.00 +8. The weekend surcharge is 2.50. It applies when the day key is SA or SU. +9. The member discount is 20 per cent. It applies when iv_member is X. Apply + the discount to the sum of the base price and the surcharge. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change or create other objects. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1135/task.json b/tasks_gen/train/G1135/task.json new file mode 100644 index 0000000..fb039dc --- /dev/null +++ b/tasks_gen/train/G1135/task.json @@ -0,0 +1,42 @@ +{ + "id": "G1135", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}POOL_TICKET" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}POOL_TICKET", + "file": "reference/pool_ticket.clas.abap", + "description": "Swimming pool ticket price", + "testclasses_file": "reference/pool_ticket.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables", + "named_types_in_signature" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1136/generation.json b/tasks_gen/train/G1136/generation.json new file mode 100644 index 0000000..d26eaae --- /dev/null +++ b/tasks_gen/train/G1136/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1136", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1136/hidden/t_d_job_hidden.clas.abap b/tasks_gen/train/G1136/hidden/t_d_job_hidden.clas.abap new file mode 100644 index 0000000..18ec843 --- /dev/null +++ b/tasks_gen/train/G1136/hidden/t_d_job_hidden.clas.abap @@ -0,0 +1,202 @@ +CLASS {{p}}t_d_job_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}job_planner. + + METHODS setup. + METHODS valid_job RETURNING VALUE(rs_job) TYPE {{p}}job_planner=>ty_job. + + METHODS standard_cost FOR TESTING. + METHODS express_cost FOR TESTING. + METHODS priority_cost FOR TESTING. + METHODS wood_cost FOR TESTING. + METHODS missing_id_raises FOR TESTING. + METHODS bad_mode_raises FOR TESTING. + METHODS bad_hours_raises FOR TESTING. + METHODS bad_count_raises FOR TESTING. + METHODS bad_group_raises FOR TESTING. + METHODS bad_date_raises FOR TESTING. + METHODS bad_value_raises FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_d_job_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}job_planner( ). + ENDMETHOD. + + METHOD valid_job. + rs_job = VALUE #( job_id = 'JOB-1000' + calc_mode = 'S' + work_hours = '10.00' + part_count = 4 + mat_group = 'ST' + due_date = '20250601' + net_value = '100.00' ). + ENDMETHOD. + + METHOD standard_cost. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + lv_exp = '750.00'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( valid_job( ) ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD express_cost. + DATA ls_job TYPE {{p}}job_planner=>ty_job. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + ls_job = valid_job( ). + ls_job-calc_mode = 'E'. + ls_job-work_hours = '2.50'. + ls_job-part_count = 2. + ls_job-mat_group = 'AL'. + ls_job-net_value = 0. + lv_exp = '241.00'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( ls_job ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD priority_cost. + DATA ls_job TYPE {{p}}job_planner=>ty_job. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + ls_job = valid_job( ). + ls_job-calc_mode = 'P'. + ls_job-work_hours = '1.00'. + ls_job-part_count = 1. + ls_job-mat_group = 'PL'. + ls_job-net_value = '10.00'. + lv_exp = '88.50'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( ls_job ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD wood_cost. + DATA ls_job TYPE {{p}}job_planner=>ty_job. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + ls_job = valid_job( ). + ls_job-work_hours = '0.50'. + ls_job-part_count = 1. + ls_job-mat_group = 'WD'. + ls_job-net_value = 0. + lv_exp = '35.00'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( ls_job ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD missing_id_raises. + DATA(ls_job) = valid_job( ). + ls_job-job_id = ''. + ls_job-calc_mode = 'Q'. + ls_job-work_hours = 0. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `NO_JOB_ID expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-no_job_id + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_mode_raises. + DATA(ls_job) = valid_job( ). + ls_job-calc_mode = 'Q'. + ls_job-work_hours = 0. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_MODE expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_mode + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_hours_raises. + DATA(ls_job) = valid_job( ). + ls_job-work_hours = 0. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_HOURS expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours + act = lx_error->get_reason( ) ). + ENDTRY. + ls_job-work_hours = '100.01'. + TRY. + mo_cut->calculate_cost( ls_job ). + cl_abap_unit_assert=>fail( `BAD_HOURS expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_cost_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours + act = lx_cost_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_count_raises. + DATA(ls_job) = valid_job( ). + ls_job-part_count = 0. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_COUNT expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_count + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_group_raises. + DATA(ls_job) = valid_job( ). + DATA lv_msg TYPE string. + ls_job-mat_group = 'XY'. + lv_msg = 'The material group is not valid.'. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_GROUP expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_group + act = lx_error->get_reason( ) ). + cl_abap_unit_assert=>assert_equals( exp = lv_msg + act = lx_error->get_message( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_date_raises. + DATA(ls_job) = valid_job( ). + ls_job-due_date = '00000000'. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_DATE expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_date + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_value_raises. + DATA(ls_job) = valid_job( ). + ls_job-net_value = -1. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_VALUE expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_value + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1136/reference/cx_job_error.clas.abap b/tasks_gen/train/G1136/reference/cx_job_error.clas.abap new file mode 100644 index 0000000..5e453e0 --- /dev/null +++ b/tasks_gen/train/G1136/reference/cx_job_error.clas.abap @@ -0,0 +1,60 @@ +CLASS {{p}}cx_job_error DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 12. + + CONSTANTS: + BEGIN OF c_reason, + no_job_id TYPE ty_reason VALUE 'NO_JOB_ID', + bad_mode TYPE ty_reason VALUE 'BAD_MODE', + bad_hours TYPE ty_reason VALUE 'BAD_HOURS', + bad_count TYPE ty_reason VALUE 'BAD_COUNT', + bad_group TYPE ty_reason VALUE 'BAD_GROUP', + bad_date TYPE ty_reason VALUE 'BAD_DATE', + bad_value TYPE ty_reason VALUE 'BAD_VALUE', + END OF c_reason. + + METHODS constructor + IMPORTING iv_reason TYPE ty_reason. + + METHODS get_reason + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS get_message + RETURNING VALUE(rv_message) TYPE string. + + PRIVATE SECTION. + DATA mv_reason TYPE ty_reason. +ENDCLASS. + + +CLASS {{p}}cx_job_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. + + METHOD get_reason. + rv_reason = mv_reason. + ENDMETHOD. + + METHOD get_message. + CASE mv_reason. + WHEN c_reason-no_job_id. + rv_message = 'The job ID is missing.'. + WHEN c_reason-bad_mode. + rv_message = 'The calculation mode is not valid.'. + WHEN c_reason-bad_hours. + rv_message = 'The work hours are not valid.'. + WHEN c_reason-bad_count. + rv_message = 'The part count is not valid.'. + WHEN c_reason-bad_group. + rv_message = 'The material group is not valid.'. + WHEN c_reason-bad_date. + rv_message = 'The due date is not valid.'. + WHEN c_reason-bad_value. + rv_message = 'The material value is not valid.'. + WHEN OTHERS. + rv_message = 'The job is not valid.'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1136/reference/job_planner.clas.abap b/tasks_gen/train/G1136/reference/job_planner.clas.abap new file mode 100644 index 0000000..5142844 --- /dev/null +++ b/tasks_gen/train/G1136/reference/job_planner.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}job_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + calc_mode TYPE c LENGTH 1, + work_hours TYPE p LENGTH 5 DECIMALS 2, + part_count TYPE i, + mat_group TYPE c LENGTH 4, + due_date TYPE d, + net_value TYPE p LENGTH 11 DECIMALS 2, + END OF ty_job. + TYPES ty_cost TYPE p LENGTH 11 DECIMALS 2. + + METHODS validate + IMPORTING is_job TYPE ty_job + RAISING {{p}}cx_job_error. + + METHODS calculate_cost + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_cost) TYPE ty_cost + RAISING {{p}}cx_job_error. + + PRIVATE SECTION. + TYPES ty_mode TYPE c LENGTH 1. + TYPES ty_group TYPE c LENGTH 4. + + METHODS fail_with + IMPORTING iv_reason TYPE {{p}}cx_job_error=>ty_reason + RAISING {{p}}cx_job_error. + + METHODS is_mode_valid + IMPORTING iv_mode TYPE ty_mode + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_hours_valid + IMPORTING iv_hours TYPE ty_job-work_hours + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_group_valid + IMPORTING iv_group TYPE ty_group + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS hour_rate + IMPORTING iv_mode TYPE ty_mode + RETURNING VALUE(rv_rate) TYPE ty_cost. + + METHODS part_rate + IMPORTING iv_group TYPE ty_group + RETURNING VALUE(rv_rate) TYPE ty_cost. +ENDCLASS. + + +CLASS {{p}}job_planner IMPLEMENTATION. + METHOD validate. + IF is_job-job_id IS INITIAL. + fail_with( {{p}}cx_job_error=>c_reason-no_job_id ). + ENDIF. + IF is_mode_valid( is_job-calc_mode ) = abap_false. + fail_with( {{p}}cx_job_error=>c_reason-bad_mode ). + ENDIF. + IF is_hours_valid( is_job-work_hours ) = abap_false. + fail_with( {{p}}cx_job_error=>c_reason-bad_hours ). + ENDIF. + IF is_job-part_count <= 0. + fail_with( {{p}}cx_job_error=>c_reason-bad_count ). + ENDIF. + IF is_group_valid( is_job-mat_group ) = abap_false. + fail_with( {{p}}cx_job_error=>c_reason-bad_group ). + ENDIF. + IF is_job-due_date IS INITIAL. + fail_with( {{p}}cx_job_error=>c_reason-bad_date ). + ENDIF. + IF is_job-net_value < 0. + fail_with( {{p}}cx_job_error=>c_reason-bad_value ). + ENDIF. + ENDMETHOD. + + METHOD calculate_cost. + validate( is_job ). + rv_cost = is_job-work_hours * hour_rate( is_job-calc_mode ) + + is_job-part_count * part_rate( is_job-mat_group ) + + is_job-net_value. + ENDMETHOD. + + METHOD fail_with. + RAISE EXCEPTION TYPE {{p}}cx_job_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_mode_valid. + rv_valid = xsdbool( iv_mode = 'S' OR iv_mode = 'E' OR iv_mode = 'P' ). + ENDMETHOD. + + METHOD is_hours_valid. + rv_valid = xsdbool( iv_hours > 0 AND iv_hours <= 100 ). + ENDMETHOD. + + METHOD is_group_valid. + rv_valid = xsdbool( iv_group = 'ST' OR iv_group = 'AL' + OR iv_group = 'PL' OR iv_group = 'WD' ). + ENDMETHOD. + + METHOD hour_rate. + CASE iv_mode. + WHEN 'S'. + rv_rate = '60.00'. + WHEN 'E'. + rv_rate = '90.00'. + WHEN 'P'. + rv_rate = '75.00'. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD part_rate. + CASE iv_group. + WHEN 'ST'. + rv_rate = '12.50'. + WHEN 'AL'. + rv_rate = '8.00'. + WHEN 'PL'. + rv_rate = '3.50'. + WHEN 'WD'. + rv_rate = '5.00'. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1136/reference/job_planner.testclasses.abap b/tasks_gen/train/G1136/reference/job_planner.testclasses.abap new file mode 100644 index 0000000..37b6fd0 --- /dev/null +++ b/tasks_gen/train/G1136/reference/job_planner.testclasses.abap @@ -0,0 +1,95 @@ +CLASS ltc_job_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}job_planner. + + METHODS setup. + METHODS valid_job RETURNING VALUE(rs_job) TYPE {{p}}job_planner=>ty_job. + + METHODS standard_cost FOR TESTING. + METHODS express_cost FOR TESTING. + METHODS missing_id FOR TESTING. + METHODS bad_hours FOR TESTING. + METHODS bad_group FOR TESTING. +ENDCLASS. + + +CLASS ltc_job_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}job_planner( ). + ENDMETHOD. + + METHOD valid_job. + rs_job = VALUE #( job_id = 'JOB-1000' + calc_mode = 'S' + work_hours = '10.00' + part_count = 4 + mat_group = 'ST' + due_date = '20250601' + net_value = '100.00' ). + ENDMETHOD. + + METHOD standard_cost. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + lv_exp = '750.00'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( valid_job( ) ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD express_cost. + DATA ls_job TYPE {{p}}job_planner=>ty_job. + DATA lv_exp TYPE {{p}}job_planner=>ty_cost. + ls_job = valid_job( ). + ls_job-calc_mode = 'E'. + ls_job-work_hours = '2.50'. + ls_job-part_count = 2. + ls_job-mat_group = 'AL'. + ls_job-net_value = 0. + lv_exp = '241.00'. + TRY. + cl_abap_unit_assert=>assert_equals( exp = lv_exp + act = mo_cut->calculate_cost( ls_job ) ). + CATCH {{p}}cx_job_error. + cl_abap_unit_assert=>fail( `unexpected exception` ). + ENDTRY. + ENDMETHOD. + + METHOD missing_id. + DATA(ls_job) = valid_job( ). + ls_job-job_id = ''. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `NO_JOB_ID expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-no_job_id + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_hours. + DATA(ls_job) = valid_job( ). + ls_job-work_hours = '101.00'. + TRY. + mo_cut->validate( ls_job ). + cl_abap_unit_assert=>fail( `BAD_HOURS expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. + + METHOD bad_group. + DATA(ls_job) = valid_job( ). + ls_job-mat_group = 'XY'. + TRY. + mo_cut->calculate_cost( ls_job ). + cl_abap_unit_assert=>fail( `BAD_GROUP expected` ). + CATCH {{p}}cx_job_error INTO DATA(lx_error). + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_group + act = lx_error->get_reason( ) ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1136/spec.md b/tasks_gen/train/G1136/spec.md new file mode 100644 index 0000000..fbd3cf3 --- /dev/null +++ b/tasks_gen/train/G1136/spec.md @@ -0,0 +1,87 @@ +# 1. Goal +Plan the cost of a production job in a metal workshop. The workshop office +checks a job before it accepts the job and calculates the planned cost of the +job. The office needs one clear reason for each job that it rejects. + +# 2. Open questions +None. + +# 3. Context +- A job order has a job ID, a calculation mode, work hours, a part count, a + material group, a due date and a material value. +- The words HOURS, MODE, ORDER, DATE, COUNT, GROUP and VALUE are reserved words + in ABAP SQL. Do not use them as names of fields, types, variables or + constants. Use the names of the contract. +- The office passes the job data to the class. No database table is necessary. + +# 4. Contract +Both classes are in package $TMP. + +## 4.1 Class {{P}}CX_JOB_ERROR +- The class is public, final, and inherits from CX_STATIC_CHECK. +- Public type TY_REASON, character 12. +- Public constant structure C_REASON with these components and values: + NO_JOB_ID, BAD_MODE, BAD_HOURS, BAD_COUNT, BAD_GROUP, BAD_DATE, BAD_VALUE. +- Public method CONSTRUCTOR, IMPORTING iv_reason TYPE ty_reason. +- Public method GET_REASON, RETURNING VALUE(rv_reason) TYPE ty_reason. +- Public method GET_MESSAGE, RETURNING VALUE(rv_message) TYPE string. +- Do not add other public components. + +## 4.2 Class {{P}}JOB_PLANNER +- The class is public, final, and has a public constructor without parameters. +- Public type TY_JOB with these components: + - job_id TYPE c LENGTH 10 + - calc_mode TYPE c LENGTH 1 + - work_hours TYPE p LENGTH 5 DECIMALS 2 + - part_count TYPE i + - mat_group TYPE c LENGTH 4 + - due_date TYPE d + - net_value TYPE p LENGTH 11 DECIMALS 2 +- Public type TY_COST, packed number, length 11, 2 decimals. +- Public method VALIDATE, IMPORTING is_job TYPE ty_job, + RAISING {{P}}CX_JOB_ERROR. +- Public method CALCULATE_COST, IMPORTING is_job TYPE ty_job, + RETURNING VALUE(rv_cost) TYPE ty_cost, RAISING {{P}}CX_JOB_ERROR. +- Do not add other public methods. + +# 5. Business rules + +## 5.1 Validation +VALIDATE checks the job in this sequence. It raises {{P}}CX_JOB_ERROR with the +reason of the first rule that fails. A rule after the first failing rule is not +checked. +1. job_id must not be empty. Reason NO_JOB_ID. +2. calc_mode must be S, E or P. Reason BAD_MODE. +3. work_hours must be greater than 0 and not greater than 100. Reason BAD_HOURS. +4. part_count must be greater than 0. Reason BAD_COUNT. +5. mat_group must be ST, AL, PL or WD. Reason BAD_GROUP. +6. due_date must not be empty. Reason BAD_DATE. +7. net_value must not be negative. Reason BAD_VALUE. + +## 5.2 Cost +CALCULATE_COST applies all rules of 5.1 first and raises the same exception. +Then it calculates the cost: + cost = work_hours * hour rate + part_count * part rate + net_value +Hour rate: S 60.00, E 90.00, P 75.00. +Part rate: ST 12.50, AL 8.00, PL 3.50, WD 5.00. + +## 5.3 Messages +GET_MESSAGE returns the message text of the reason: +- NO_JOB_ID: The job ID is missing. +- BAD_MODE: The calculation mode is not valid. +- BAD_HOURS: The work hours are not valid. +- BAD_COUNT: The part count is not valid. +- BAD_GROUP: The material group is not valid. +- BAD_DATE: The due date is not valid. +- BAD_VALUE: The material value is not valid. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. Pass structures by reference. No comment that restates the code. +- Out of scope: no database table, no report, no ALV list, no CDS view. + +# 7. Acceptance +- Both classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}JOB_PLANNER. diff --git a/tasks_gen/train/G1136/task.json b/tasks_gen/train/G1136/task.json new file mode 100644 index 0000000..cb7cacb --- /dev/null +++ b/tasks_gen/train/G1136/task.json @@ -0,0 +1,57 @@ +{ + "id": "G1136", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_JOB_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}JOB_PLANNER" + } + ], + "out_of_scope": [ + "database tables", + "CDS views", + "reports", + "ALV lists", + "other exception classes" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_D_JOB_HIDDEN", + "file": "hidden/t_d_job_hidden.clas.abap", + "description": "Hidden tests for the job validation and the job cost" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_JOB_ERROR", + "file": "reference/cx_job_error.clas.abap", + "description": "Class-based exception with a reason and a message text" + }, + { + "type": "CLAS", + "name": "{{P}}JOB_PLANNER", + "file": "reference/job_planner.clas.abap", + "description": "Job validation and job cost calculation", + "testclasses_file": "reference/job_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_reserved_word_names" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1137/faulty/m0_tattoo_fee.func.abap b/tasks_gen/train/G1137/faulty/m0_tattoo_fee.func.abap new file mode 100644 index 0000000..85d6cc9 --- /dev/null +++ b/tasks_gen/train/G1137/faulty/m0_tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_true. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 2 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions - 1 ) * lv_further. + + IF cv_fee < lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/faulty/m1_tattoo_fee.func.abap b/tasks_gen/train/G1137/faulty/m1_tattoo_fee.func.abap new file mode 100644 index 0000000..3988ce8 --- /dev/null +++ b/tasks_gen/train/G1137/faulty/m1_tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_false. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 3 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions - 1 ) * lv_further. + + IF cv_fee < lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/faulty/m2_tattoo_fee.func.abap b/tasks_gen/train/G1137/faulty/m2_tattoo_fee.func.abap new file mode 100644 index 0000000..c3b09e1 --- /dev/null +++ b/tasks_gen/train/G1137/faulty/m2_tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_false. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 2 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions + 1 ) * lv_further. + + IF cv_fee < lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/faulty/m3_tattoo_fee.func.abap b/tasks_gen/train/G1137/faulty/m3_tattoo_fee.func.abap new file mode 100644 index 0000000..ccbf583 --- /dev/null +++ b/tasks_gen/train/G1137/faulty/m3_tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_false. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 2 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions - 1 ) / lv_further. + + IF cv_fee < lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/faulty/m4_tattoo_fee.func.abap b/tasks_gen/train/G1137/faulty/m4_tattoo_fee.func.abap new file mode 100644 index 0000000..29262c5 --- /dev/null +++ b/tasks_gen/train/G1137/faulty/m4_tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_false. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 2 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions - 1 ) * lv_further. + + IF cv_fee >= lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/generation.json b/tasks_gen/train/G1137/generation.json new file mode 100644 index 0000000..54523dd --- /dev/null +++ b/tasks_gen/train/G1137/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1137", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0156 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1137/hidden/t42_hidden.clas.abap b/tasks_gen/train/G1137/hidden/t42_hidden.clas.abap new file mode 100644 index 0000000..dc2ea6b --- /dev/null +++ b/tasks_gen/train/G1137/hidden/t42_hidden.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}t42_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + iv_fee TYPE decfloat34 DEFAULT 0 + EXPORTING + ev_deposit TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS line_minimum FOR TESTING. + METHODS line_two_sessions FOR TESTING. + METHODS line_three_sessions FOR TESTING. + METHODS line_maximum FOR TESTING. + METHODS shade_minimum FOR TESTING. + METHODS shade_two_sessions FOR TESTING. + METHODS shade_maximum FOR TESTING. + METHODS other_style_zero FOR TESTING. + METHODS invalid_sessions FOR TESTING. + METHODS deposit_long_project FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}TATTOO_FEE' + EXPORTING + iv_style = iv_style + iv_sessions = iv_sessions + IMPORTING + ev_deposit = ev_deposit + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_sessions = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD line_minimum. + call( EXPORTING iv_style = `LINE` iv_sessions = 1 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '150.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD line_two_sessions. + call( EXPORTING iv_style = `LINE` iv_sessions = 2 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '200.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD line_three_sessions. + call( EXPORTING iv_style = `LINE` iv_sessions = 3 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '266.67' ) act = lv_fee ). + ENDMETHOD. + + METHOD line_maximum. + call( EXPORTING iv_style = `LINE` iv_sessions = 12 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '800.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD shade_minimum. + call( EXPORTING iv_style = `SHADE` iv_sessions = 1 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '110.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD shade_two_sessions. + call( EXPORTING iv_style = `SHADE` iv_sessions = 2 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '146.66' ) act = lv_fee ). + ENDMETHOD. + + METHOD shade_maximum. + call( EXPORTING iv_style = `SHADE` iv_sessions = 12 IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '600.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD other_style_zero. + call( EXPORTING iv_style = `XX` iv_sessions = 3 + IMPORTING ev_fee = DATA(lv_fee) ev_deposit = DATA(lv_deposit) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_deposit ). + ENDMETHOD. + + METHOD invalid_sessions. + call( EXPORTING iv_style = `LINE` iv_sessions = 0 IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD deposit_long_project. + DATA lv_fee TYPE decfloat34. + DATA lv_deposit TYPE abap_bool. + call( EXPORTING iv_style = `LINE` iv_sessions = 6 + IMPORTING ev_fee = lv_fee ev_deposit = lv_deposit ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_deposit ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '466.68' ) act = lv_fee ). + call( EXPORTING iv_style = `LINE` iv_sessions = 7 + IMPORTING ev_fee = lv_fee ev_deposit = lv_deposit ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_deposit ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '533.35' ) act = lv_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1137/mutation.json b/tasks_gen/train/G1137/mutation.json new file mode 100644 index 0000000..7142e09 --- /dev/null +++ b/tasks_gen/train/G1137/mutation.json @@ -0,0 +1,78 @@ +{ + "task": "G1137", + "mutants": [ + { + "object": "{{P}}TATTOO_FEE", + "mutant": "line 25: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "DEPOSIT_LONG_PROJECT", + "OTHER_STYLE_ZERO" + ] + }, + { + "object": "{{P}}TATTOO_FEE", + "mutant": "line 46: 2 -> 3 (const)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "DEPOSIT_LONG_PROJECT", + "LINE_MAXIMUM", + "LINE_THREE_SESSIONS", + "LINE_TWO_SESSIONS", + "SHADE_MAXIMUM", + "SHADE_TWO_SESSIONS" + ] + }, + { + "object": "{{P}}TATTOO_FEE", + "mutant": "line 47: - -> + (arith)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "DEPOSIT_LONG_PROJECT", + "LINE_MINIMUM", + "LINE_THREE_SESSIONS", + "LINE_TWO_SESSIONS", + "SHADE_MINIMUM", + "SHADE_TWO_SESSIONS" + ] + }, + { + "object": "{{P}}TATTOO_FEE", + "mutant": "line 47: * -> / (arith)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "DEPOSIT_LONG_PROJECT", + "LINE_MAXIMUM", + "LINE_THREE_SESSIONS", + "LINE_TWO_SESSIONS", + "OTHER_STYLE_ZERO", + "SHADE_MAXIMUM", + "SHADE_TWO_SESSIONS" + ] + }, + { + "object": "{{P}}TATTOO_FEE", + "mutant": "line 49: < -> >= (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "DEPOSIT_LONG_PROJECT", + "LINE_MAXIMUM", + "LINE_MINIMUM", + "LINE_THREE_SESSIONS", + "LINE_TWO_SESSIONS", + "SHADE_MAXIMUM", + "SHADE_MINIMUM", + "SHADE_TWO_SESSIONS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1137/reference/t42_test.clas.abap b/tasks_gen/train/G1137/reference/t42_test.clas.abap new file mode 100644 index 0000000..b823902 --- /dev/null +++ b/tasks_gen/train/G1137/reference/t42_test.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}t42_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS line_fee FOR TESTING. + METHODS shade_fee FOR TESTING. + METHODS fee_limits FOR TESTING. + METHODS deposit_flag FOR TESTING. + METHODS invalid_sessions FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_deposit TYPE abap_bool. + CALL FUNCTION '{{P}}TATTOO_FEE' + EXPORTING + iv_style = iv_style + iv_sessions = iv_sessions + IMPORTING + ev_deposit = lv_deposit + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_sessions = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD line_fee. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '200.00' ) + act = fee_for( iv_style = `LINE` iv_sessions = 2 ) ). + ENDMETHOD. + + METHOD shade_fee. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '146.66' ) + act = fee_for( iv_style = `SHADE` iv_sessions = 2 ) ). + ENDMETHOD. + + METHOD fee_limits. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '150.00' ) + act = fee_for( iv_style = `LINE` iv_sessions = 1 ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '800.00' ) + act = fee_for( iv_style = `LINE` iv_sessions = 20 ) ). + ENDMETHOD. + + METHOD deposit_flag. + DATA lv_deposit TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}TATTOO_FEE' + EXPORTING + iv_style = `LINE` + iv_sessions = 7 + IMPORTING + ev_deposit = lv_deposit + CHANGING + cv_fee = lv_fee. + cl_abap_unit_assert=>assert_true( lv_deposit ). + ENDMETHOD. + + METHOD invalid_sessions. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}TATTOO_FEE' + EXPORTING + iv_style = `LINE` + iv_sessions = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_sessions = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1137/reference/tattoo_fee.func.abap b/tasks_gen/train/G1137/reference/tattoo_fee.func.abap new file mode 100644 index 0000000..43a3612 --- /dev/null +++ b/tasks_gen/train/G1137/reference/tattoo_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}tattoo_fee + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + CONSTANTS lc_first_line TYPE decfloat34 VALUE '133.33'. + CONSTANTS lc_first_shade TYPE decfloat34 VALUE '97.77'. + CONSTANTS lc_min_line TYPE decfloat34 VALUE '150.00'. + CONSTANTS lc_max_line TYPE decfloat34 VALUE '800.00'. + CONSTANTS lc_min_shade TYPE decfloat34 VALUE '110.00'. + CONSTANTS lc_max_shade TYPE decfloat34 VALUE '600.00'. + CONSTANTS lc_long_project TYPE i VALUE 6. + + DATA lv_first TYPE decfloat34. + DATA lv_minimum TYPE decfloat34. + DATA lv_maximum TYPE decfloat34. + DATA lv_further TYPE decfloat34. + + ev_deposit = abap_false. + + IF iv_sessions < 1. + RAISE invalid_sessions. + ENDIF. + + CASE iv_style. + WHEN 'LINE'. + lv_first = lc_first_line. + lv_minimum = lc_min_line. + lv_maximum = lc_max_line. + WHEN 'SHADE'. + lv_first = lc_first_shade. + lv_minimum = lc_min_shade. + lv_maximum = lc_max_shade. + WHEN OTHERS. + lv_first = 0. + lv_minimum = 0. + lv_maximum = 0. + ENDCASE. + + lv_further = round( val = lv_first / 2 dec = 2 mode = cl_abap_math=>round_half_up ). + cv_fee = lv_first + ( iv_sessions - 1 ) * lv_further. + + IF cv_fee < lv_minimum. + cv_fee = lv_minimum. + ELSEIF cv_fee > lv_maximum. + cv_fee = lv_maximum. + ENDIF. + + IF iv_sessions > lc_long_project. + ev_deposit = abap_true. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/seed/tattoo_sample.func.abap b/tasks_gen/train/G1137/seed/tattoo_sample.func.abap new file mode 100644 index 0000000..a951847 --- /dev/null +++ b/tasks_gen/train/G1137/seed/tattoo_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}tattoo_sample + IMPORTING + iv_style TYPE string + iv_sessions TYPE i + EXPORTING + ev_deposit TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_sessions. + + ev_deposit = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1137/spec.md b/tasks_gen/train/G1137/spec.md new file mode 100644 index 0000000..8ee77ef --- /dev/null +++ b/tasks_gen/train/G1137/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A tattoo studio prices tattoo projects. The reception desk calls one function +module for each project. The function module calculates the studio fee. +The function module must use the same interface as the sample pricing module, +because the reception desk calls all pricing modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_TATTOO exists in package $TMP. +- Function module {{P}}TATTOO_SAMPLE exists in this group. It is the sample + pricing module. Its interface is the interface that every pricing module must + use. Read the interface of {{P}}TATTOO_SAMPLE before you write code. +- A project has a style code and a number of sessions. A project has one + session or many sessions. + +# 4. Contract +- Create the function module {{P}}TATTOO_FEE in function group {{P}}FG_TATTOO. +- Use exactly the interface of {{P}}TATTOO_SAMPLE: the same parameter names, + the same parameter kinds, the same types, and the same exceptions. + +# 5. Business rules +1. If the number of sessions is less than 1, raise the exception + INVALID_SESSIONS. The sample module declares this exception. +2. Set the deposit flag to false, except in rule 6. +3. Style code LINE: the first session costs 133.33. Every further session + costs half of 133.33, rounded half up to 2 decimals. The fee is the sum of + the first session and all further sessions. Then apply the minimum fee + 150.00 and the maximum fee 800.00. +4. Style code SHADE: the first session costs 97.77. Every further session + costs half of 97.77, rounded half up to 2 decimals. The fee is the sum of + the first session and all further sessions. Then apply the minimum fee + 110.00 and the maximum fee 600.00. +5. Other style codes: the fee is 0. +6. If the number of sessions is greater than 6, set the deposit flag to true. + This rule applies to every style code. The fee does not change. + +# 6. Constraints +- Release target: 8.16. +- Use Clean ABAP. Keep the code short and readable. +- Out of scope: do not change {{P}}TATTOO_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1137/task.json b/tasks_gen/train/G1137/task.json new file mode 100644 index 0000000..2e140cc --- /dev/null +++ b/tasks_gen/train/G1137/task.json @@ -0,0 +1,80 @@ +{ + "id": "G1137", + "category": "F", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_TATTOO", + "description": "Tattoo studio pricing modules" + }, + { + "type": "FUNC", + "name": "{{P}}TATTOO_SAMPLE", + "functionGroup": "{{P}}FG_TATTOO", + "file": "seed/tattoo_sample.func.abap", + "description": "Sample pricing module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}TATTOO_FEE", + "functionGroup": "{{P}}FG_TATTOO", + "params": [ + { + "name": "iv_style", + "type": "string" + }, + { + "name": "iv_sessions", + "type": "i" + }, + { + "name": "ev_deposit", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}TATTOO_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T42_HIDDEN", + "file": "hidden/t42_hidden.clas.abap", + "description": "T42 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}TATTOO_FEE", + "functionGroup": "{{P}}FG_TATTOO", + "file": "reference/tattoo_fee.func.abap", + "description": "Tattoo studio pricing module" + }, + { + "type": "CLAS", + "name": "{{P}}T42_TEST", + "file": "reference/t42_test.clas.abap", + "description": "T42 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1138/faulty/m0_line_output.prog.abap b/tasks_gen/train/G1138/faulty/m0_line_output.prog.abap new file mode 100644 index 0000000..3d176d3 --- /dev/null +++ b/tasks_gen/train/G1138/faulty/m0_line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date <> iv_date + AND shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles + ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops + 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/faulty/m1_line_output.prog.abap b/tasks_gen/train/G1138/faulty/m1_line_output.prog.abap new file mode 100644 index 0000000..2c687d9 --- /dev/null +++ b/tasks_gen/train/G1138/faulty/m1_line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date = iv_date + OR shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles + ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops + 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/faulty/m2_line_output.prog.abap b/tasks_gen/train/G1138/faulty/m2_line_output.prog.abap new file mode 100644 index 0000000..24393f7 --- /dev/null +++ b/tasks_gen/train/G1138/faulty/m2_line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date = iv_date + AND shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min <= 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles + ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops + 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/faulty/m3_line_output.prog.abap b/tasks_gen/train/G1138/faulty/m3_line_output.prog.abap new file mode 100644 index 0000000..b0f37fe --- /dev/null +++ b/tasks_gen/train/G1138/faulty/m3_line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date = iv_date + AND shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles - ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops + 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/faulty/m4_line_output.prog.abap b/tasks_gen/train/G1138/faulty/m4_line_output.prog.abap new file mode 100644 index 0000000..ad00557 --- /dev/null +++ b/tasks_gen/train/G1138/faulty/m4_line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date = iv_date + AND shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles + ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops - 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/generation.json b/tasks_gen/train/G1138/generation.json new file mode 100644 index 0000000..300cd27 --- /dev/null +++ b/tasks_gen/train/G1138/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1138", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 71.7, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1138/hidden/line_out_hid.clas.abap b/tasks_gen/train/G1138/hidden/line_out_hid.clas.abap new file mode 100644 index 0000000..7a8f2d6 --- /dev/null +++ b/tasks_gen/train/G1138/hidden/line_out_hid.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}line_out_hid DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + METHODS run + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_lines FOR TESTING. + METHODS sort_order FOR TESTING. + METHODS bottles_sum FOR TESTING. + METHODS stops_count FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS shift_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}line_out_hid IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}line_output WITH p_date = iv_date WITH s_shift IN it_shift AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_lines. + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( run( '20260401' ) ) ). + ENDMETHOD. + + METHOD sort_order. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `L4` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LINE_NO` ) ). + cl_abap_unit_assert=>assert_equals( exp = `L1` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LINE_NO` ) ). + cl_abap_unit_assert=>assert_equals( exp = `L2` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LINE_NO` ) ). + cl_abap_unit_assert=>assert_equals( exp = `L3` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `LINE_NO` ) ). + ENDMETHOD. + + METHOD bottles_sum. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `1600` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOTTLES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1500` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `BOTTLES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `300` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `BOTTLES` ) ). + ENDMETHOD. + + METHOD stops_count. + DATA(lr_data) = run( '20260401' ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `STOPS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `STOPS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `0` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `STOPS` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( '20260402' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `L1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LINE_NO` ) ). + cl_abap_unit_assert=>assert_equals( exp = `900` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOTTLES` ) ). + ENDMETHOD. + + METHOD shift_filter. + DATA(lr_data) = run( iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `L4` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LINE_NO` ) ). + cl_abap_unit_assert=>assert_equals( exp = `L1` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LINE_NO` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/mutation.json b/tasks_gen/train/G1138/mutation.json new file mode 100644 index 0000000..6850778 --- /dev/null +++ b/tasks_gen/train/G1138/mutation.json @@ -0,0 +1,64 @@ +{ + "task": "G1138", + "mutants": [ + { + "object": "{{P}}LINE_OUTPUT", + "mutant": "line 42: = -> <> (eq)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_LINES", + "BOTTLES_SUM", + "DATE_FILTER", + "SHIFT_FILTER", + "SORT_ORDER", + "STOPS_COUNT" + ] + }, + { + "object": "{{P}}LINE_OUTPUT", + "mutant": "line 43: AND -> OR (logic)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "BOTTLES_SUM", + "DATE_FILTER", + "SHIFT_FILTER", + "SORT_ORDER" + ] + }, + { + "object": "{{P}}LINE_OUTPUT", + "mutant": "line 56: > -> <= (rel)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "STOPS_COUNT" + ] + }, + { + "object": "{{P}}LINE_OUTPUT", + "mutant": "line 61: + -> - (arith)", + "status": "killed", + "hidden": "3/6", + "failed_tests": [ + "BOTTLES_SUM", + "SORT_ORDER", + "STOPS_COUNT" + ] + }, + { + "object": "{{P}}LINE_OUTPUT", + "mutant": "line 63: + -> - (arith)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "STOPS_COUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1138/reference/line_output.prog.abap b/tasks_gen/train/G1138/reference/line_output.prog.abap new file mode 100644 index 0000000..278959b --- /dev/null +++ b/tasks_gen/train/G1138/reference/line_output.prog.abap @@ -0,0 +1,139 @@ +REPORT {{p}}line_output. + +TYPES: BEGIN OF ty_record, + line_no TYPE {{p}}bottle-line_no, + shift_no TYPE {{p}}bottle-shift_no, + bottles TYPE {{p}}bottle-bottles, + stop_min TYPE {{p}}bottle-stop_min, + END OF ty_record, + tt_record TYPE STANDARD TABLE OF ty_record WITH DEFAULT KEY. + +TYPES tt_shift TYPE RANGE OF {{p}}bottle-shift_no. + +DATA gv_shift TYPE {{p}}bottle-shift_no. + +PARAMETERS p_date TYPE d OBLIGATORY. +SELECT-OPTIONS s_shift FOR gv_shift. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: BEGIN OF ty_line, + line_no TYPE {{p}}bottle-line_no, + bottles TYPE {{p}}bottle-bottles, + stops TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY. + METHODS read_records + IMPORTING iv_date TYPE d + it_shift TYPE tt_shift + RETURNING VALUE(rt_records) TYPE tt_record. + METHODS build_lines + IMPORTING it_records TYPE tt_record + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD read_records. + SELECT line_no shift_no bottles stop_min + FROM {{p}}bottle + INTO CORRESPONDING FIELDS OF TABLE rt_records + WHERE prod_date = iv_date + AND shift_no IN it_shift. + ENDMETHOD. + + METHOD build_lines. + DATA ls_record TYPE ty_record. + DATA ls_line TYPE ty_line. + + LOOP AT it_records INTO ls_record. + READ TABLE rt_lines INTO ls_line WITH KEY line_no = ls_record-line_no. + IF sy-subrc <> 0. + CLEAR ls_line. + ls_line-line_no = ls_record-line_no. + ls_line-bottles = ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = 1. + ENDIF. + APPEND ls_line TO rt_lines. + ELSE. + ls_line-bottles = ls_line-bottles + ls_record-bottles. + IF ls_record-stop_min > 0. + ls_line-stops = ls_line-stops + 1. + ENDIF. + MODIFY rt_lines FROM ls_line INDEX sy-tabix. + ENDIF. + ENDLOOP. + + SORT rt_lines BY bottles DESCENDING line_no ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( + IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_records) = go_report->read_records( iv_date = p_date it_shift = s_shift[] ). + DATA(gt_lines) = go_report->build_lines( gt_records ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_bottles FOR TESTING. + METHODS counts_stops FOR TESTING. + METHODS filters_shift FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}BOTTLE' ) ) ). + DATA lt_bottle TYPE STANDARD TABLE OF {{p}}bottle WITH DEFAULT KEY. + lt_bottle = VALUE #( + ( rec_id = 1 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 0 ) + ( rec_id = 2 line_no = 'A' prod_date = '20260401' shift_no = '1' bottles = 50 stop_min = 10 ) + ( rec_id = 3 line_no = 'B' prod_date = '20260401' shift_no = '2' bottles = 200 stop_min = 0 ) + ( rec_id = 4 line_no = 'C' prod_date = '20260402' shift_no = '1' bottles = 300 stop_min = 0 ) ). + go_env->insert_test_data( lt_bottle ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_bottles. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = lt_lines[ 1 ]-bottles ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_lines[ 2 ]-bottles ). + ENDMETHOD. + + METHOD counts_stops. + DATA(lt_records) = NEW lcl_report( )->read_records( iv_date = '20260401' it_shift = VALUE #( ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-stops ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 2 ]-stops ). + ENDMETHOD. + + METHOD filters_shift. + DATA(lt_records) = NEW lcl_report( )->read_records( + iv_date = '20260401' + it_shift = VALUE #( ( sign = 'I' option = 'EQ' low = '1' ) ) ). + DATA(lt_lines) = NEW lcl_report( )->build_lines( lt_records ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lt_lines[ 1 ]-line_no ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/seed/bottle.tabl.asabap b/tasks_gen/train/G1138/seed/bottle.tabl.asabap new file mode 100644 index 0000000..f3e1b58 --- /dev/null +++ b/tasks_gen/train/G1138/seed/bottle.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Bottle filling records' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}bottle { + key client : abap.clnt not null; + key rec_id : abap.int4 not null; + line_no : abap.char(4); + prod_date : abap.dats; + shift_no : abap.char(1); + bottles : abap.int4; + stop_min : abap.int4; +} diff --git a/tasks_gen/train/G1138/seed/seed_bottle.clas.abap b/tasks_gen/train/G1138/seed/seed_bottle.clas.abap new file mode 100644 index 0000000..6b7d383 --- /dev/null +++ b/tasks_gen/train/G1138/seed/seed_bottle.clas.abap @@ -0,0 +1,20 @@ +CLASS {{p}}seed_bottle DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}seed_bottle IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}bottle. + INSERT {{p}}bottle FROM TABLE @( VALUE #( + ( rec_id = 1 line_no = 'L1' prod_date = '20260401' shift_no = '1' bottles = 1000 stop_min = 0 ) + ( rec_id = 2 line_no = 'L1' prod_date = '20260401' shift_no = '1' bottles = 500 stop_min = 15 ) + ( rec_id = 3 line_no = 'L2' prod_date = '20260401' shift_no = '2' bottles = 800 stop_min = 0 ) + ( rec_id = 4 line_no = 'L2' prod_date = '20260401' shift_no = '2' bottles = 700 stop_min = 5 ) + ( rec_id = 5 line_no = 'L3' prod_date = '20260401' shift_no = '3' bottles = 300 stop_min = 0 ) + ( rec_id = 6 line_no = 'L1' prod_date = '20260402' shift_no = '1' bottles = 900 stop_min = 0 ) + ( rec_id = 7 line_no = 'L4' prod_date = '20260401' shift_no = '1' bottles = 1500 stop_min = 0 ) + ( rec_id = 8 line_no = 'L4' prod_date = '20260401' shift_no = '1' bottles = 100 stop_min = 30 ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1138/spec.md b/tasks_gen/train/G1138/spec.md new file mode 100644 index 0000000..9b035ec --- /dev/null +++ b/tasks_gen/train/G1138/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A beverage bottling plant fills bottles on several filling lines. Show the output of +each filling line for one production day in an ALV list. The shift supervisor uses +the list to compare the filling lines. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}BOTTLE exists in package $TMP. It contains data. + Fields: + - REC_ID (INT4, key): the number of the record. + - LINE_NO (CHAR 4): the number of the filling line. + - PROD_DATE (DATS): the production day. + - SHIFT_NO (CHAR 1): the shift. + - BOTTLES (INT4): the number of bottles that the line filled in this record. + - STOP_MIN (INT4): the minutes that the line stopped in this record. + +# 4. Contract +- Create the executable program {{P}}LINE_OUTPUT in package $TMP. +- Selection screen: + - Parameter P_DATE, type DATS, mandatory: the production day to show. + - Select-option S_SHIFT for the shift number. +- Show the result with CL_SALV_TABLE. The output table has these columns with these + names: + - LINE_NO: the number of the filling line. + - BOTTLES: the total number of bottles that the line filled. + - STOPS: the number of records of the line that have a stop. + +# 5. Business rules +1. Include a record only if PROD_DATE is equal to P_DATE and SHIFT_NO is in S_SHIFT. +2. Show one line for each filling line that has at least one included record. +3. BOTTLES is the sum of the BOTTLES values of the included records of the line. +4. STOPS is the number of included records of the line where STOP_MIN is greater + than zero. +5. Sort the lines by BOTTLES, the highest number first. If two lines have the same + BOTTLES value, sort them by LINE_NO in ascending order. + +# 6. Constraints +- Release target: 7.40 SP05. Use only syntax that this release supports. +- Coding standards: Clean ABAP. Read the records with one database access. +- Out of scope: do not change {{P}}BOTTLE. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1138/task.json b/tasks_gen/train/G1138/task.json new file mode 100644 index 0000000..99725e2 --- /dev/null +++ b/tasks_gen/train/G1138/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1138", + "category": "G", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v740sp05", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}BOTTLE", + "file": "seed/bottle.tabl.asabap", + "description": "Bottle filling records of the filling lines" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_BOTTLE", + "file": "seed/seed_bottle.clas.abap", + "description": "Seed data for {{P}}BOTTLE", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}LINE_OUTPUT", + "parameters": [ + "p_date", + "s_shift" + ] + } + ], + "out_of_scope": [ + "{{P}}BOTTLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}LINE_OUT_HID", + "file": "hidden/line_out_hid.clas.abap", + "description": "Hidden tests for {{P}}LINE_OUTPUT" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}LINE_OUTPUT", + "file": "reference/line_output.prog.abap", + "description": "Output of each filling line for one production day" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1139/generation.json b/tasks_gen/train/G1139/generation.json new file mode 100644 index 0000000..02c8920 --- /dev/null +++ b/tasks_gen/train/G1139/generation.json @@ -0,0 +1,23 @@ +{ + "id": "G1139", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0173 (similarity spec 0.64, rules 0.35, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1139/reference/loan_check.func.abap b/tasks_gen/train/G1139/reference/loan_check.func.abap new file mode 100644 index 0000000..9d3a4d0 --- /dev/null +++ b/tasks_gen/train/G1139/reference/loan_check.func.abap @@ -0,0 +1,36 @@ +FUNCTION {{p}}loan_check + IMPORTING + iv_fragile TYPE abap_bool + iv_insured TYPE abap_bool + iv_days TYPE i + EXPORTING + ev_approved TYPE abap_bool + ev_reason TYPE string + EXCEPTIONS + invalid_days. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + IF iv_days > 90. + ev_approved = abap_false. + ev_reason = 'TOO_LONG'. + RETURN. + ENDIF. + + " Open point: rules 3 and 4 give opposite priority to the insurance rule and + " the fragility rule for an insured fragile exhibit. This reference applies + " rule 3 (the insurance rule first). + IF iv_insured = abap_true. + ev_approved = abap_true. + ev_reason = 'INSURED'. + ELSEIF iv_fragile = abap_true. + ev_approved = abap_false. + ev_reason = 'FRAGILE'. + ELSE. + ev_approved = abap_true. + ev_reason = 'OK'. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1139/reference/loan_test.clas.abap b/tasks_gen/train/G1139/reference/loan_test.clas.abap new file mode 100644 index 0000000..a2f5fb6 --- /dev/null +++ b/tasks_gen/train/G1139/reference/loan_test.clas.abap @@ -0,0 +1,91 @@ +CLASS {{p}}loan_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS check + IMPORTING + iv_fragile TYPE abap_bool + iv_insured TYPE abap_bool + iv_days TYPE i + EXPORTING + ev_approved TYPE abap_bool + ev_reason TYPE string + ev_subrc TYPE sysubrc. + METHODS invalid_days FOR TESTING. + METHODS too_long FOR TESTING. + METHODS insured_approved FOR TESTING. + METHODS fragile_rejected FOR TESTING. + METHODS plain_approved FOR TESTING. +ENDCLASS. + + +CLASS {{p}}loan_test IMPLEMENTATION. + METHOD check. + CALL FUNCTION '{{P}}LOAN_CHECK' + EXPORTING + iv_fragile = iv_fragile + iv_insured = iv_insured + iv_days = iv_days + IMPORTING + ev_approved = ev_approved + ev_reason = ev_reason + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD invalid_days. + check( + EXPORTING + iv_days = 0 + IMPORTING + ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD too_long. + check( + EXPORTING + iv_days = 100 + IMPORTING + ev_approved = DATA(lv_approved) + ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_false( lv_approved ). + cl_abap_unit_assert=>assert_equals( exp = `TOO_LONG` act = lv_reason ). + ENDMETHOD. + + METHOD insured_approved. + check( + EXPORTING + iv_insured = abap_true + iv_days = 30 + IMPORTING + ev_approved = DATA(lv_approved) + ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_true( lv_approved ). + cl_abap_unit_assert=>assert_equals( exp = `INSURED` act = lv_reason ). + ENDMETHOD. + + METHOD fragile_rejected. + check( + EXPORTING + iv_fragile = abap_true + iv_days = 30 + IMPORTING + ev_approved = DATA(lv_approved) + ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_false( lv_approved ). + cl_abap_unit_assert=>assert_equals( exp = `FRAGILE` act = lv_reason ). + ENDMETHOD. + + METHOD plain_approved. + check( + EXPORTING + iv_days = 30 + IMPORTING + ev_approved = DATA(lv_approved) + ev_reason = DATA(lv_reason) ). + cl_abap_unit_assert=>assert_true( lv_approved ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = lv_reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1139/seed/loan_sample.func.abap b/tasks_gen/train/G1139/seed/loan_sample.func.abap new file mode 100644 index 0000000..218e210 --- /dev/null +++ b/tasks_gen/train/G1139/seed/loan_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}loan_sample + IMPORTING + iv_fragile TYPE abap_bool + iv_insured TYPE abap_bool + iv_days TYPE i + EXPORTING + ev_approved TYPE abap_bool + ev_reason TYPE string + EXCEPTIONS + invalid_days. + + ev_approved = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1139/spec.md b/tasks_gen/train/G1139/spec.md new file mode 100644 index 0000000..3b4d46a --- /dev/null +++ b/tasks_gen/train/G1139/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +A museum lends exhibits to other museums. Before the museum approves a loan +request, the loan desk calls one check module for each request. The check module +must have the same interface as the sample check module, because the loan desk +calls all check modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_LOAN exists in package $TMP. +- Function module {{P}}LOAN_SAMPLE exists in this group. It is the sample check + module. Its interface is the interface that all check modules must use. + +# 4. Contract +- Create the function module {{P}}LOAN_CHECK in function group {{P}}FG_LOAN. +- Use exactly the interface of {{P}}LOAN_SAMPLE: the same parameter names, kinds, + types, and exceptions. + +# 5. Business rules +1. If the loan period in days is less than 1, raise the exception INVALID_DAYS. +2. If the loan period is more than 90 days, the request is rejected. The reason + is 'TOO_LONG'. No other rule applies. +3. If the exhibit is insured, the request is approved. The reason is 'INSURED'. + The insurance rule has priority over the fragility rule. +4. If the exhibit is fragile, the request is rejected. The reason is 'FRAGILE'. + The fragility rule has priority over the insurance rule. +5. If the exhibit is neither insured nor fragile, the request is approved. The + reason is 'OK'. +6. Rules 3, 4 and 5 apply to a loan period of 1 to 90 days. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep each method below 40 statements. +- Out of scope: do not change {{P}}LOAN_SAMPLE. + +# 7. Acceptance +- The function module is active and has no syntax error. +- The tests of the check module pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1139/task.json b/tasks_gen/train/G1139/task.json new file mode 100644 index 0000000..8dc7d34 --- /dev/null +++ b/tasks_gen/train/G1139/task.json @@ -0,0 +1,94 @@ +{ + "id": "G1139", + "category": "H", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_LOAN", + "description": "Exhibit loan check function group" + }, + { + "type": "FUNC", + "name": "{{P}}LOAN_SAMPLE", + "functionGroup": "{{P}}FG_LOAN", + "file": "seed/loan_sample.func.abap", + "description": "Sample check module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}LOAN_CHECK", + "functionGroup": "{{P}}FG_LOAN", + "params": [ + { + "name": "iv_fragile", + "type": "abap_bool" + }, + { + "name": "iv_insured", + "type": "abap_bool" + }, + { + "name": "iv_days", + "type": "i" + }, + { + "name": "ev_approved", + "type": "abap_bool" + }, + { + "name": "ev_reason", + "type": "string" + } + ] + } + ], + "out_of_scope": [ + "{{P}}LOAN_SAMPLE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}LOAN_CHECK", + "functionGroup": "{{P}}FG_LOAN", + "file": "reference/loan_check.func.abap", + "description": "Exhibit loan check module" + }, + { + "type": "CLAS", + "name": "{{P}}LOAN_TEST", + "file": "reference/loan_test.clas.abap", + "description": "Own tests of the check module" + } + ], + "craft_checks": [ + "method_length" + ], + "expected_gap": { + "description": "Business rules 3 and 4 give opposite priority to the insurance rule and the fragility rule, so a loan request for an insured fragile exhibit cannot be decided.", + "keywords": [ + [ + "insured", + "fragile" + ], + [ + "priority", + "opposite" + ], + [ + "contradict", + "rule" + ] + ] + } +} \ No newline at end of file diff --git a/tasks_gen/train/G1140/hidden/mill_hidden.clas.abap b/tasks_gen/train/G1140/hidden/mill_hidden.clas.abap new file mode 100644 index 0000000..2a8b923 --- /dev/null +++ b/tasks_gen/train/G1140/hidden/mill_hidden.clas.abap @@ -0,0 +1,104 @@ +CLASS {{p}}mill_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_region TYPE RANGE OF {{p}}grove-region. + METHODS run + IMPORTING iv_from TYPE d + it_region TYPE tt_region OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS amount_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_regions FOR TESTING. + METHODS sorted_by_litres FOR TESTING. + METHODS litres_and_kilos FOR TESTING. + METHODS region_filter FOR TESTING. + METHODS two_region_filter FOR TESTING. + METHODS date_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}mill_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}oil_report + WITH p_from = iv_from + WITH s_reg IN it_region + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD amount_of. + DATA lv_amount TYPE p LENGTH 15 DECIMALS 2. + lv_amount = value_of( ir_data = ir_data iv_row = iv_row iv_col = iv_col ). + rv_value = CONV string( lv_amount ). + ENDMETHOD. + + METHOD all_regions. + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( run( '20260101' ) ) ). + ENDMETHOD. + + METHOD sorted_by_litres. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `NORTH` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `SOUTH` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REGION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `WEST` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `REGION` ) ). + ENDMETHOD. + + METHOD litres_and_kilos. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `290.00` act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1500` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `KILOS` ) ). + ENDMETHOD. + + METHOD region_filter. + DATA(lr_data) = run( iv_from = '20260101' + it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `150.00` act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD two_region_filter. + DATA(lr_data) = run( iv_from = '20260101' + it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'WEST' ) + ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `SOUTH` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `WEST` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REGION` ) ). + ENDMETHOD. + + METHOD date_filter. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( run( '20260201' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1140/mutation.json b/tasks_gen/train/G1140/mutation.json new file mode 100644 index 0000000..e3722be --- /dev/null +++ b/tasks_gen/train/G1140/mutation.json @@ -0,0 +1,44 @@ +{ + "task": "T50", + "mutants": [ + { + "object": "{{P}}OIL_REPORT", + "mutant": "line 33: >= -> < (rel)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_REGIONS", + "SORTED_BY_LITRES", + "LITRES_AND_KILOS", + "REGION_FILTER", + "TWO_REGION_FILTER", + "DATE_FILTER" + ] + }, + { + "object": "{{P}}OIL_REPORT", + "mutant": "line 34: AND -> OR (logic)", + "status": "killed", + "hidden": "3/6", + "failed_tests": [ + "REGION_FILTER", + "TWO_REGION_FILTER", + "DATE_FILTER" + ] + }, + { + "object": "{{P}}OIL_REPORT", + "mutant": "line 36: DESCENDING -> ASCENDING (order)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "SORTED_BY_LITRES", + "TWO_REGION_FILTER" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1140/reference/oil_report.prog.abap b/tasks_gen/train/G1140/reference/oil_report.prog.abap new file mode 100644 index 0000000..2a908cc --- /dev/null +++ b/tasks_gen/train/G1140/reference/oil_report.prog.abap @@ -0,0 +1,105 @@ +REPORT {{p}}oil_report. + +DATA gv_region TYPE {{p}}grove-region. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_reg FOR gv_region. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + region TYPE {{p}}grove-region, + litres TYPE {{p}}press-litres, + kilos TYPE {{p}}press-kilos, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_region TYPE RANGE OF {{p}}grove-region. + METHODS collect + IMPORTING iv_from TYPE d + it_region TYPE tt_region + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS show + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT gr~region, + SUM( pr~litres ) AS litres, + SUM( pr~kilos ) AS kilos + FROM {{p}}press AS pr + INNER JOIN {{p}}grove AS gr ON gr~grove_id = pr~grove_id + WHERE pr~press_date >= @iv_from + AND gr~region IN @it_region + GROUP BY gr~region + ORDER BY litres DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD show. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from it_region = s_reg[] ). + go_report->show( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS litres_per_region FOR TESTING. + METHODS region_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}GROVE' ) ( '{{P}}PRESS' ) ) ). + DATA lt_grove TYPE STANDARD TABLE OF {{p}}grove WITH EMPTY KEY. + lt_grove = VALUE #( ( grove_id = 1 grove_name = 'A' region = 'NORTH' variety = 'ARBEQ' ) + ( grove_id = 2 grove_name = 'B' region = 'NORTH' variety = 'ARBEQ' ) + ( grove_id = 3 grove_name = 'C' region = 'SOUTH' variety = 'PICUA' ) ). + go_env->insert_test_data( lt_grove ). + DATA lt_press TYPE STANDARD TABLE OF {{p}}press WITH EMPTY KEY. + lt_press = VALUE #( ( press_id = 1 grove_id = 1 press_date = '20260110' kilos = 1000 litres = '200.00' ) + ( press_id = 2 grove_id = 2 press_date = '20260111' kilos = 500 litres = '90.00' ) + ( press_id = 3 grove_id = 3 press_date = '20260112' kilos = 800 litres = '150.00' ) ). + go_env->insert_test_data( lt_press ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD litres_per_region. + DATA lv_expected TYPE {{p}}press-litres. + lv_expected = '290.00'. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' it_region = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'NORTH' act = lt_lines[ 1 ]-region ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ). + cl_abap_unit_assert=>assert_equals( exp = 1500 act = lt_lines[ 1 ]-kilos ). + ENDMETHOD. + + METHOD region_filter. + DATA lv_expected TYPE {{p}}press-litres. + lv_expected = '150.00'. + DATA(lt_lines) = NEW lcl_report( )->collect( + iv_from = '20260101' + it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1140/seed/grove.tabl.asabap b/tasks_gen/train/G1140/seed/grove.tabl.asabap new file mode 100644 index 0000000..2584539 --- /dev/null +++ b/tasks_gen/train/G1140/seed/grove.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Olive grove' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}grove { + key client : abap.clnt not null; + key grove_id : abap.int4 not null; + grove_name : abap.char(30); + region : abap.char(8); + variety : abap.char(10); +} diff --git a/tasks_gen/train/G1140/seed/mill_seed.clas.abap b/tasks_gen/train/G1140/seed/mill_seed.clas.abap new file mode 100644 index 0000000..c40aab6 --- /dev/null +++ b/tasks_gen/train/G1140/seed/mill_seed.clas.abap @@ -0,0 +1,24 @@ +CLASS {{p}}mill_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}mill_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}press. + DELETE FROM {{p}}grove. + INSERT {{p}}grove FROM TABLE @( VALUE #( + ( grove_id = 1 grove_name = 'North Ridge' region = 'NORTH' variety = 'ARBEQ' ) + ( grove_id = 2 grove_name = 'Sunny Slope' region = 'NORTH' variety = 'ARBEQ' ) + ( grove_id = 3 grove_name = 'Hill Top' region = 'SOUTH' variety = 'PICUA' ) + ( grove_id = 4 grove_name = 'Valley Farm' region = 'EAST' variety = 'PICUA' ) + ( grove_id = 5 grove_name = 'Old Mill' region = 'WEST' variety = 'KORON' ) ) ). + INSERT {{p}}press FROM TABLE @( VALUE #( + ( press_id = 1 grove_id = 1 press_date = '20260110' kilos = 1000 litres = '200.00' ) + ( press_id = 2 grove_id = 2 press_date = '20260210' kilos = 500 litres = '90.00' ) + ( press_id = 3 grove_id = 3 press_date = '20260115' kilos = 800 litres = '150.00' ) + ( press_id = 4 grove_id = 4 press_date = '20251231' kilos = 600 litres = '120.00' ) + ( press_id = 5 grove_id = 5 press_date = '20260301' kilos = 400 litres = '80.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1140/seed/press.tabl.asabap b/tasks_gen/train/G1140/seed/press.tabl.asabap new file mode 100644 index 0000000..b632a14 --- /dev/null +++ b/tasks_gen/train/G1140/seed/press.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Olive pressing' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}press { + key client : abap.clnt not null; + key press_id : abap.int4 not null; + grove_id : abap.int4 not null; + press_date : abap.dats; + kilos : abap.int4; + litres : abap.dec(9,2); +} diff --git a/tasks_gen/train/G1140/spec.md b/tasks_gen/train/G1140/spec.md new file mode 100644 index 0000000..7de5331 --- /dev/null +++ b/tasks_gen/train/G1140/spec.md @@ -0,0 +1,41 @@ +# 1. Goal +An olive oil mill presses the olives that its member groves deliver. Show the +oil output of each growing region for a period in an ALV list. The mill manager +uses the list to compare the regions. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}GROVE is in package $TMP and contains data. + Fields: GROVE_ID (INT4, key), GROVE_NAME (CHAR 30), REGION (CHAR 8), + VARIETY (CHAR 10). +- Table {{P}}PRESS is in package $TMP and contains data. + Fields: PRESS_ID (INT4, key), GROVE_ID (INT4), PRESS_DATE (DATS), + KILOS (INT4), LITRES (DEC 9,2). + +# 4. Contract +- Create the executable program {{P}}OIL_REPORT in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first pressing date to include. + - Select-option S_REG for the growing region. +- Show the result with CL_SALV_TABLE. The output table has these columns with +these names: REGION, LITRES, KILOS. + +# 5. Business rules +1. Include a pressing only if PRESS_DATE is on or after P_FROM and the region of +the grove that delivered the olives is in S_REG. +2. LITRES of a region is the sum of the litres of all included pressings. +3. KILOS of a region is the sum of the kilos of all included pressings. +4. Show one line for each region with at least one included pressing. +5. Sort the lines by LITRES, the highest value first. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}GROVE and {{P}}PRESS. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1140/task.json b/tasks_gen/train/G1140/task.json new file mode 100644 index 0000000..932f3a3 --- /dev/null +++ b/tasks_gen/train/G1140/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1140", + "category": "B", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}GROVE", + "file": "seed/grove.tabl.asabap", + "description": "Olive groves of the mill members" + }, + { + "type": "TABL", + "name": "{{P}}PRESS", + "file": "seed/press.tabl.asabap", + "description": "Pressing records of the olive mill" + }, + { + "type": "CLAS", + "name": "{{P}}MILL_SEED", + "file": "seed/mill_seed.clas.abap", + "description": "Seed data for the olive mill", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}OIL_REPORT", + "parameters": [ + "p_from", + "s_reg" + ] + } + ], + "out_of_scope": [ + "{{P}}GROVE", + "{{P}}PRESS" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}MILL_HIDDEN", + "file": "hidden/mill_hidden.clas.abap", + "description": "Hidden tests for the olive oil output report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}OIL_REPORT", + "file": "reference/oil_report.prog.abap", + "description": "Oil output per growing region" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1141/faulty/m1_exhib_cmp.prog.abap b/tasks_gen/train/G1141/faulty/m1_exhib_cmp.prog.abap new file mode 100644 index 0000000..e83dd6e --- /dev/null +++ b/tasks_gen/train/G1141/faulty/m1_exhib_cmp.prog.abap @@ -0,0 +1,134 @@ +REPORT {{p}}exhib_cmp. + +TYPES ty_exhib_id TYPE {{p}}exhib-exhib_id. + +DATA gv_exhib TYPE ty_exhib_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_exhib FOR gv_exhib. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + exhib_id TYPE {{p}}exhib-exhib_id, + exhib_name TYPE {{p}}exhib-exhib_name, + plan_vis TYPE i, + act_vis TYPE i, + gap TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_exhib TYPE RANGE OF {{p}}exhib-exhib_id. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_exhib TYPE tt_exhib + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT exhib_id, exhib_name, plan_vis + FROM {{p}}exhib + WHERE exhib_id IN @it_exhib + INTO TABLE @DATA(lt_plan). + + SELECT exhib_id, SUM( visitors ) AS act_vis + FROM {{p}}visit + WHERE visit_date BETWEEN @iv_from AND @iv_to + OR exhib_id IN @it_exhib + GROUP BY exhib_id + INTO TABLE @DATA(lt_act). + + DATA lt_result TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY exhib_id. + LOOP AT lt_plan INTO DATA(ls_plan). + INSERT VALUE #( exhib_id = ls_plan-exhib_id + exhib_name = ls_plan-exhib_name + plan_vis = ls_plan-plan_vis ) INTO TABLE lt_result. + ENDLOOP. + + LOOP AT lt_act INTO DATA(ls_act). + READ TABLE lt_result ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY exhib_id = ls_act-exhib_id. + IF sy-subrc = 0. + -act_vis = ls_act-act_vis. + ELSE. + INSERT VALUE #( exhib_id = ls_act-exhib_id + act_vis = ls_act-act_vis ) INTO TABLE lt_result. + ENDIF. + ENDLOOP. + + rt_lines = CORRESPONDING #( lt_result ). + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -act_vis < -plan_vis. + -gap = -plan_vis - -act_vis. + ENDIF. + ENDLOOP. + SORT rt_lines BY act_vis DESCENDING exhib_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from iv_to = p_to it_exhib = s_exhib[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS merged_lines FOR TESTING. + METHODS unplanned_line FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}EXHIB' ) ( '{{P}}VISIT' ) ) ). + DATA lt_exhib TYPE STANDARD TABLE OF {{p}}exhib WITH EMPTY KEY. + lt_exhib = VALUE #( ( exhib_id = 'A1' exhib_name = 'One' plan_vis = 100 ) ). + go_env->insert_test_data( lt_exhib ). + DATA lt_visit TYPE STANDARD TABLE OF {{p}}visit WITH EMPTY KEY. + lt_visit = VALUE #( ( visit_id = 1 exhib_id = 'A1' visit_date = '20260110' visitors = 60 ) + ( visit_id = 2 exhib_id = 'A1' visit_date = '20260111' visitors = 50 ) + ( visit_id = 3 exhib_id = 'B2' visit_date = '20260110' visitors = 20 ) ). + go_env->insert_test_data( lt_visit ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD merged_lines. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 1 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = lt_lines[ 1 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_lines[ 1 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-gap ). + ENDMETHOD. + + METHOD unplanned_line. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 2 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_lines[ 2 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-gap ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/faulty/m2_exhib_cmp.prog.abap b/tasks_gen/train/G1141/faulty/m2_exhib_cmp.prog.abap new file mode 100644 index 0000000..7756eb7 --- /dev/null +++ b/tasks_gen/train/G1141/faulty/m2_exhib_cmp.prog.abap @@ -0,0 +1,134 @@ +REPORT {{p}}exhib_cmp. + +TYPES ty_exhib_id TYPE {{p}}exhib-exhib_id. + +DATA gv_exhib TYPE ty_exhib_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_exhib FOR gv_exhib. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + exhib_id TYPE {{p}}exhib-exhib_id, + exhib_name TYPE {{p}}exhib-exhib_name, + plan_vis TYPE i, + act_vis TYPE i, + gap TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_exhib TYPE RANGE OF {{p}}exhib-exhib_id. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_exhib TYPE tt_exhib + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT exhib_id, exhib_name, plan_vis + FROM {{p}}exhib + WHERE exhib_id IN @it_exhib + INTO TABLE @DATA(lt_plan). + + SELECT exhib_id, SUM( visitors ) AS act_vis + FROM {{p}}visit + WHERE visit_date BETWEEN @iv_from AND @iv_to + AND exhib_id IN @it_exhib + GROUP BY exhib_id + INTO TABLE @DATA(lt_act). + + DATA lt_result TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY exhib_id. + LOOP AT lt_plan INTO DATA(ls_plan). + INSERT VALUE #( exhib_id = ls_plan-exhib_id + exhib_name = ls_plan-exhib_name + plan_vis = ls_plan-plan_vis ) INTO TABLE lt_result. + ENDLOOP. + + LOOP AT lt_act INTO DATA(ls_act). + READ TABLE lt_result ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY exhib_id = ls_act-exhib_id. + IF sy-subrc = 0. + -act_vis = ls_act-act_vis. + ELSE. + INSERT VALUE #( exhib_id = ls_act-exhib_id + act_vis = ls_act-act_vis ) INTO TABLE lt_result. + ENDIF. + ENDLOOP. + + rt_lines = CORRESPONDING #( lt_result ). + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -act_vis >= -plan_vis. + -gap = -plan_vis - -act_vis. + ENDIF. + ENDLOOP. + SORT rt_lines BY act_vis DESCENDING exhib_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from iv_to = p_to it_exhib = s_exhib[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS merged_lines FOR TESTING. + METHODS unplanned_line FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}EXHIB' ) ( '{{P}}VISIT' ) ) ). + DATA lt_exhib TYPE STANDARD TABLE OF {{p}}exhib WITH EMPTY KEY. + lt_exhib = VALUE #( ( exhib_id = 'A1' exhib_name = 'One' plan_vis = 100 ) ). + go_env->insert_test_data( lt_exhib ). + DATA lt_visit TYPE STANDARD TABLE OF {{p}}visit WITH EMPTY KEY. + lt_visit = VALUE #( ( visit_id = 1 exhib_id = 'A1' visit_date = '20260110' visitors = 60 ) + ( visit_id = 2 exhib_id = 'A1' visit_date = '20260111' visitors = 50 ) + ( visit_id = 3 exhib_id = 'B2' visit_date = '20260110' visitors = 20 ) ). + go_env->insert_test_data( lt_visit ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD merged_lines. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 1 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = lt_lines[ 1 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_lines[ 1 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-gap ). + ENDMETHOD. + + METHOD unplanned_line. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 2 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_lines[ 2 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-gap ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/faulty/m3_exhib_cmp.prog.abap b/tasks_gen/train/G1141/faulty/m3_exhib_cmp.prog.abap new file mode 100644 index 0000000..4c036ee --- /dev/null +++ b/tasks_gen/train/G1141/faulty/m3_exhib_cmp.prog.abap @@ -0,0 +1,134 @@ +REPORT {{p}}exhib_cmp. + +TYPES ty_exhib_id TYPE {{p}}exhib-exhib_id. + +DATA gv_exhib TYPE ty_exhib_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_exhib FOR gv_exhib. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + exhib_id TYPE {{p}}exhib-exhib_id, + exhib_name TYPE {{p}}exhib-exhib_name, + plan_vis TYPE i, + act_vis TYPE i, + gap TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_exhib TYPE RANGE OF {{p}}exhib-exhib_id. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_exhib TYPE tt_exhib + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT exhib_id, exhib_name, plan_vis + FROM {{p}}exhib + WHERE exhib_id IN @it_exhib + INTO TABLE @DATA(lt_plan). + + SELECT exhib_id, SUM( visitors ) AS act_vis + FROM {{p}}visit + WHERE visit_date BETWEEN @iv_from AND @iv_to + AND exhib_id IN @it_exhib + GROUP BY exhib_id + INTO TABLE @DATA(lt_act). + + DATA lt_result TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY exhib_id. + LOOP AT lt_plan INTO DATA(ls_plan). + INSERT VALUE #( exhib_id = ls_plan-exhib_id + exhib_name = ls_plan-exhib_name + plan_vis = ls_plan-plan_vis ) INTO TABLE lt_result. + ENDLOOP. + + LOOP AT lt_act INTO DATA(ls_act). + READ TABLE lt_result ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY exhib_id = ls_act-exhib_id. + IF sy-subrc = 0. + -act_vis = ls_act-act_vis. + ELSE. + INSERT VALUE #( exhib_id = ls_act-exhib_id + act_vis = ls_act-act_vis ) INTO TABLE lt_result. + ENDIF. + ENDLOOP. + + rt_lines = CORRESPONDING #( lt_result ). + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -act_vis < -plan_vis. + -gap = -plan_vis + -act_vis. + ENDIF. + ENDLOOP. + SORT rt_lines BY act_vis DESCENDING exhib_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from iv_to = p_to it_exhib = s_exhib[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS merged_lines FOR TESTING. + METHODS unplanned_line FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}EXHIB' ) ( '{{P}}VISIT' ) ) ). + DATA lt_exhib TYPE STANDARD TABLE OF {{p}}exhib WITH EMPTY KEY. + lt_exhib = VALUE #( ( exhib_id = 'A1' exhib_name = 'One' plan_vis = 100 ) ). + go_env->insert_test_data( lt_exhib ). + DATA lt_visit TYPE STANDARD TABLE OF {{p}}visit WITH EMPTY KEY. + lt_visit = VALUE #( ( visit_id = 1 exhib_id = 'A1' visit_date = '20260110' visitors = 60 ) + ( visit_id = 2 exhib_id = 'A1' visit_date = '20260111' visitors = 50 ) + ( visit_id = 3 exhib_id = 'B2' visit_date = '20260110' visitors = 20 ) ). + go_env->insert_test_data( lt_visit ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD merged_lines. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 1 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = lt_lines[ 1 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_lines[ 1 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-gap ). + ENDMETHOD. + + METHOD unplanned_line. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 2 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_lines[ 2 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-gap ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/generation.json b/tasks_gen/train/G1141/generation.json new file mode 100644 index 0000000..a3c2136 --- /dev/null +++ b/tasks_gen/train/G1141/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1141", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 91.1, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1141/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1141/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..3679600 --- /dev/null +++ b/tasks_gen/train/G1141/hidden/t31_hidden.clas.abap @@ -0,0 +1,139 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_exhib TYPE RANGE OF {{p}}exhib-exhib_id. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_exhib TYPE tt_exhib OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_lines FOR TESTING. + METHODS sorted_by_actual FOR TESTING. + METHODS actual_totals FOR TESTING. + METHODS gap_value FOR TESTING. + METHODS unplanned_shown FOR TESTING. + METHODS plan_without_visits FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS exhib_filter FOR TESTING. + METHODS name_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}exhib_cmp WITH p_from = iv_from WITH p_to = iv_to + WITH s_exhib IN it_exhib AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_lines. + cl_abap_unit_assert=>assert_equals( + exp = 5 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ). + ENDMETHOD. + + METHOD sorted_by_actual. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( + exp = `E300` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `4300` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ACT_VIS` ) ). + ENDMETHOD. + + METHOD actual_totals. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( + exp = `E100` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `2100` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `ACT_VIS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `5000` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `PLAN_VIS` ) ). + ENDMETHOD. + + METHOD gap_value. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( + exp = `2900` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `GAP` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `0` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `GAP` ) ). + ENDMETHOD. + + METHOD unplanned_shown. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( + exp = `E500` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `0` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `PLAN_VIS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `700` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `ACT_VIS` ) ). + cl_abap_unit_assert=>assert_initial( + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `EXHIB_NAME` ) + msg = `EXHIB_NAME of an exhibition without a plan must be empty` ). + ENDMETHOD. + + METHOD plan_without_visits. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20251231' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `E100` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `E200` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `0` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `ACT_VIS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `3000` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `GAP` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260105' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `E100` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `EXHIB_ID` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `1200` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ACT_VIS` ) ). + ENDMETHOD. + + METHOD exhib_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ( sign = 'I' option = 'EQ' low = 'E100' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `2100` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ACT_VIS` ) ). + ENDMETHOD. + + METHOD name_shown. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( + exp = `Ancient Egypt` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `EXHIB_NAME` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/mutation.json b/tasks_gen/train/G1141/mutation.json new file mode 100644 index 0000000..07421a9 --- /dev/null +++ b/tasks_gen/train/G1141/mutation.json @@ -0,0 +1,50 @@ +{ + "task": "G1141", + "mutants": [ + { + "object": "{{P}}EXHIB_CMP", + "mutant": "line 41: AND -> OR (logic)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}EXHIB_CMP", + "mutant": "line 42: AND -> OR (logic)", + "status": "killed", + "hidden": "2/9", + "failed_tests": [ + "ACTUAL_TOTALS", + "DATE_FILTER", + "EXHIB_FILTER", + "GAP_VALUE", + "NAME_SHOWN", + "PLAN_WITHOUT_VISITS", + "SORTED_BY_ACTUAL" + ] + }, + { + "object": "{{P}}EXHIB_CMP", + "mutant": "line 66: < -> >= (rel)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "GAP_VALUE", + "PLAN_WITHOUT_VISITS" + ] + }, + { + "object": "{{P}}EXHIB_CMP", + "mutant": "line 67: - -> + (arith)", + "status": "killed", + "hidden": "8/9", + "failed_tests": [ + "GAP_VALUE" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1141/reference/exhib_cmp.prog.abap b/tasks_gen/train/G1141/reference/exhib_cmp.prog.abap new file mode 100644 index 0000000..800f246 --- /dev/null +++ b/tasks_gen/train/G1141/reference/exhib_cmp.prog.abap @@ -0,0 +1,134 @@ +REPORT {{p}}exhib_cmp. + +TYPES ty_exhib_id TYPE {{p}}exhib-exhib_id. + +DATA gv_exhib TYPE ty_exhib_id. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_exhib FOR gv_exhib. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + exhib_id TYPE {{p}}exhib-exhib_id, + exhib_name TYPE {{p}}exhib-exhib_name, + plan_vis TYPE i, + act_vis TYPE i, + gap TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_exhib TYPE RANGE OF {{p}}exhib-exhib_id. + METHODS collect + IMPORTING iv_from TYPE d + iv_to TYPE d + it_exhib TYPE tt_exhib + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD collect. + SELECT exhib_id, exhib_name, plan_vis + FROM {{p}}exhib + WHERE exhib_id IN @it_exhib + INTO TABLE @DATA(lt_plan). + + SELECT exhib_id, SUM( visitors ) AS act_vis + FROM {{p}}visit + WHERE visit_date BETWEEN @iv_from AND @iv_to + AND exhib_id IN @it_exhib + GROUP BY exhib_id + INTO TABLE @DATA(lt_act). + + DATA lt_result TYPE HASHED TABLE OF ty_line WITH UNIQUE KEY exhib_id. + LOOP AT lt_plan INTO DATA(ls_plan). + INSERT VALUE #( exhib_id = ls_plan-exhib_id + exhib_name = ls_plan-exhib_name + plan_vis = ls_plan-plan_vis ) INTO TABLE lt_result. + ENDLOOP. + + LOOP AT lt_act INTO DATA(ls_act). + READ TABLE lt_result ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY exhib_id = ls_act-exhib_id. + IF sy-subrc = 0. + -act_vis = ls_act-act_vis. + ELSE. + INSERT VALUE #( exhib_id = ls_act-exhib_id + act_vis = ls_act-act_vis ) INTO TABLE lt_result. + ENDIF. + ENDLOOP. + + rt_lines = CORRESPONDING #( lt_result ). + LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(). + IF -act_vis < -plan_vis. + -gap = -plan_vis - -act_vis. + ENDIF. + ENDLOOP. + SORT rt_lines BY act_vis DESCENDING exhib_id ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->collect( iv_from = p_from iv_to = p_to it_exhib = s_exhib[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS merged_lines FOR TESTING. + METHODS unplanned_line FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}EXHIB' ) ( '{{P}}VISIT' ) ) ). + DATA lt_exhib TYPE STANDARD TABLE OF {{p}}exhib WITH EMPTY KEY. + lt_exhib = VALUE #( ( exhib_id = 'A1' exhib_name = 'One' plan_vis = 100 ) ). + go_env->insert_test_data( lt_exhib ). + DATA lt_visit TYPE STANDARD TABLE OF {{p}}visit WITH EMPTY KEY. + lt_visit = VALUE #( ( visit_id = 1 exhib_id = 'A1' visit_date = '20260110' visitors = 60 ) + ( visit_id = 2 exhib_id = 'A1' visit_date = '20260111' visitors = 50 ) + ( visit_id = 3 exhib_id = 'B2' visit_date = '20260110' visitors = 20 ) ). + go_env->insert_test_data( lt_visit ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD merged_lines. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_lines[ 1 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = lt_lines[ 1 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_lines[ 1 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 1 ]-gap ). + ENDMETHOD. + + METHOD unplanned_line. + DATA(lt_lines) = NEW lcl_report( )->collect( iv_from = '20260101' iv_to = '20260131' + it_exhib = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B2' act = lt_lines[ 2 ]-exhib_id ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-plan_vis ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_lines[ 2 ]-act_vis ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 2 ]-gap ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/seed/exhib.tabl.asabap b/tasks_gen/train/G1141/seed/exhib.tabl.asabap new file mode 100644 index 0000000..7a9064a --- /dev/null +++ b/tasks_gen/train/G1141/seed/exhib.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Exhibition plan' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}exhib { + key client : abap.clnt not null; + key exhib_id : abap.char(6) not null; + exhib_name : abap.char(30); + plan_vis : abap.int4; +} diff --git a/tasks_gen/train/G1141/seed/t31_seed.clas.abap b/tasks_gen/train/G1141/seed/t31_seed.clas.abap new file mode 100644 index 0000000..3ee708c --- /dev/null +++ b/tasks_gen/train/G1141/seed/t31_seed.clas.abap @@ -0,0 +1,26 @@ +CLASS {{p}}t31_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}t31_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}visit. + DELETE FROM {{p}}exhib. + INSERT {{p}}exhib FROM TABLE @( VALUE #( + ( exhib_id = 'E100' exhib_name = 'Ancient Egypt' plan_vis = 5000 ) + ( exhib_id = 'E200' exhib_name = 'Modern Art' plan_vis = 3000 ) + ( exhib_id = 'E300' exhib_name = 'Dinosaurs' plan_vis = 4000 ) + ( exhib_id = 'E400' exhib_name = 'Space' plan_vis = 2000 ) ) ). + INSERT {{p}}visit FROM TABLE @( VALUE #( + ( visit_id = 1 exhib_id = 'E100' visit_date = '20260105' visitors = 1200 ) + ( visit_id = 2 exhib_id = 'E100' visit_date = '20260106' visitors = 900 ) + ( visit_id = 3 exhib_id = 'E200' visit_date = '20260105' visitors = 400 ) + ( visit_id = 4 exhib_id = 'E300' visit_date = '20260107' visitors = 2500 ) + ( visit_id = 5 exhib_id = 'E300' visit_date = '20260108' visitors = 1800 ) + ( visit_id = 6 exhib_id = 'E400' visit_date = '20260109' visitors = 300 ) + ( visit_id = 7 exhib_id = 'E500' visit_date = '20260110' visitors = 700 ) + ( visit_id = 8 exhib_id = 'E100' visit_date = '20251220' visitors = 5000 ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1141/seed/visit.tabl.asabap b/tasks_gen/train/G1141/seed/visit.tabl.asabap new file mode 100644 index 0000000..7c6a4c2 --- /dev/null +++ b/tasks_gen/train/G1141/seed/visit.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Daily visitors' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}visit { + key client : abap.clnt not null; + key visit_id : abap.int4 not null; + exhib_id : abap.char(6); + visit_date : abap.dats; + visitors : abap.int4; +} diff --git a/tasks_gen/train/G1141/spec.md b/tasks_gen/train/G1141/spec.md new file mode 100644 index 0000000..0f015a9 --- /dev/null +++ b/tasks_gen/train/G1141/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +Show a comparison of the planned and the actual visitor numbers of the +exhibitions of a museum. The museum director uses the list to see which +exhibitions reached their plan and how many visitors are still missing. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}EXHIB exists in package $TMP. It contains the plan of the +exhibitions. It contains data. + Fields: EXHIB_ID (CHAR 6, key), EXHIB_NAME (CHAR 30), PLAN_VIS (INT4). +- Table {{P}}VISIT exists in package $TMP. It contains the daily visitor +numbers. It contains data. + Fields: VISIT_ID (INT4, key), EXHIB_ID (CHAR 6), VISIT_DATE (DATS), + VISITORS (INT4). + +# 4. Contract +- Create the executable program {{P}}EXHIB_CMP in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first visit date to include. + - Parameter P_TO, type DATS, mandatory: the last visit date to include. + - Select-option S_EXHIB for the exhibition ID. +- Show the result with CL_SALV_TABLE. The output table has these columns, with +these names: EXHIB_ID, EXHIB_NAME, PLAN_VIS, ACT_VIS, GAP. +PLAN_VIS, ACT_VIS and GAP are integers. + +# 5. Business rules +1. Include an exhibition only if its EXHIB_ID is in S_EXHIB. +2. Include a visit only if its EXHIB_ID is in S_EXHIB and its VISIT_DATE is on + or after P_FROM and on or before P_TO. +3. Show one line for each exhibition that has a plan row or at least one + included visit. +4. ACT_VIS is the sum of the VISITORS of the included visits of the exhibition. + If the exhibition has no included visit, ACT_VIS is 0. +5. PLAN_VIS is the planned number of the exhibition. If the exhibition has no + plan row, PLAN_VIS is 0 and EXHIB_NAME is empty. +6. GAP is PLAN_VIS minus ACT_VIS if ACT_VIS is less than PLAN_VIS. In all other + cases GAP is 0. +7. Sort the lines by ACT_VIS, the highest first. If two lines have the same + ACT_VIS, sort them by EXHIB_ID in ascending order. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Read each table with one database access. +- Out of scope: do not change {{P}}EXHIB and {{P}}VISIT. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1141/task.json b/tasks_gen/train/G1141/task.json new file mode 100644 index 0000000..4fb1bc2 --- /dev/null +++ b/tasks_gen/train/G1141/task.json @@ -0,0 +1,69 @@ +{ + "id": "G1141", + "category": "C", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}EXHIB", + "file": "seed/exhib.tabl.asabap", + "description": "Plan of the museum exhibitions" + }, + { + "type": "TABL", + "name": "{{P}}VISIT", + "file": "seed/visit.tabl.asabap", + "description": "Daily visitor numbers" + }, + { + "type": "CLAS", + "name": "{{P}}T31_SEED", + "file": "seed/t31_seed.clas.abap", + "description": "T31 seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}EXHIB_CMP", + "parameters": [ + "p_from", + "p_to", + "s_exhib" + ] + } + ], + "out_of_scope": [ + "{{P}}EXHIB", + "{{P}}VISIT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}EXHIB_CMP", + "file": "reference/exhib_cmp.prog.abap", + "description": "Planned and actual visitors per exhibition" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "internal_table_kind", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1142/generation.json b/tasks_gen/train/G1142/generation.json new file mode 100644 index 0000000..54d690e --- /dev/null +++ b/tasks_gen/train/G1142/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1142", + "pool": "train", + "object_type": "FUNC", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/hut_fee_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/hut_fee_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"calc_fee\"", + "hidden/hut_fee_hidden.clas.abap line 116: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"calc_fee\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 81.7, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1142/hidden/hut_fee_hidden.clas.abap b/tasks_gen/train/G1142/hidden/hut_fee_hidden.clas.abap new file mode 100644 index 0000000..e3edae9 --- /dev/null +++ b/tasks_gen/train/G1142/hidden/hut_fee_hidden.clas.abap @@ -0,0 +1,141 @@ +CLASS {{p}}hut_fee_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_of + IMPORTING + iv_type TYPE string + iv_nights TYPE i + iv_member TYPE abap_bool DEFAULT abap_false + iv_breakfast TYPE abap_bool DEFAULT abap_false + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS rc_of + IMPORTING + iv_type TYPE string + iv_nights TYPE i + RETURNING + VALUE(rv_subrc) TYPE sysubrc. + METHODS adult_one_night FOR TESTING. + METHODS child_with_breakfast FOR TESTING. + METHODS member_halves_rate FOR TESTING. + METHODS dog_ignores_member FOR TESTING. + METHODS dog_gets_no_breakfast FOR TESTING. + METHODS unknown_type_as_adult FOR TESTING. + METHODS lower_case_type FOR TESTING. + METHODS three_nights_discount FOR TESTING. + METHODS seven_nights_discount FOR TESTING. + METHODS two_nights_no_discount FOR TESTING. + METHODS member_full_stay FOR TESTING. + METHODS invalid_nights_raise FOR TESTING. +ENDCLASS. + + +CLASS {{p}}hut_fee_hidden IMPLEMENTATION. + + METHOD fee_of. + DATA lv_subrc TYPE sysubrc. + CALL FUNCTION '{{P}}HUT_FEE' + EXPORTING + iv_guest_type = iv_type + iv_nights = iv_nights + iv_member = iv_member + iv_breakfast = iv_breakfast + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + lv_subrc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + ENDMETHOD. + + METHOD rc_of. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}HUT_FEE' + EXPORTING + iv_guest_type = iv_type + iv_nights = iv_nights + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + rv_subrc = sy-subrc. + ENDMETHOD. + + METHOD adult_one_night. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '28.00' ) + act = fee_of( iv_type = `ADULT` iv_nights = 1 ) ). + ENDMETHOD. + + METHOD child_with_breakfast. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '47.00' ) + act = fee_of( iv_type = `CHILD` iv_nights = 2 iv_breakfast = abap_true ) ). + ENDMETHOD. + + METHOD member_halves_rate. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '28.00' ) + act = fee_of( iv_type = `ADULT` iv_nights = 2 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD dog_ignores_member. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '10.00' ) + act = fee_of( iv_type = `DOG` iv_nights = 2 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD dog_gets_no_breakfast. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '10.00' ) + act = fee_of( iv_type = `DOG` iv_nights = 2 iv_breakfast = abap_true ) ). + ENDMETHOD. + + METHOD unknown_type_as_adult. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '28.00' ) + act = fee_of( iv_type = `STUDENT` iv_nights = 1 ) ). + ENDMETHOD. + + METHOD lower_case_type. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '7.00' ) + act = fee_of( iv_type = `child` iv_nights = 1 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD three_nights_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '75.60' ) + act = fee_of( iv_type = `ADULT` iv_nights = 3 ) ). + ENDMETHOD. + + METHOD seven_nights_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '156.80' ) + act = fee_of( iv_type = `ADULT` iv_nights = 7 ) ). + ENDMETHOD. + + METHOD two_nights_no_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '56.00' ) + act = fee_of( iv_type = `ADULT` iv_nights = 2 ) ). + ENDMETHOD. + + METHOD member_full_stay. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '144.90' ) + act = fee_of( iv_type = `ADULT` iv_nights = 7 iv_member = abap_true iv_breakfast = abap_true ) ). + ENDMETHOD. + + METHOD invalid_nights_raise. + cl_abap_unit_assert=>assert_equals( + exp = 1 + act = rc_of( iv_type = `ADULT` iv_nights = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 1 + act = rc_of( iv_type = `ADULT` iv_nights = -2 ) ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1142/mutation.json b/tasks_gen/train/G1142/mutation.json new file mode 100644 index 0000000..637d56b --- /dev/null +++ b/tasks_gen/train/G1142/mutation.json @@ -0,0 +1,76 @@ +{ + "task": "E21", + "mutants": [ + { + "object": "{{P}}HUT_FEE", + "mutant": "line 43: c_member_share 50 -> 40 (const)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "MEMBER_HALVES_RATE", + "LOWER_CASE_TYPE", + "MEMBER_FULL_STAY" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 49: c_share_long 80 -> 70 (const)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "SEVEN_NIGHTS_DISCOUNT", + "MEMBER_FULL_STAY" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 50: c_stay_short 3 -> 4 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "THREE_NIGHTS_DISCOUNT" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 42: remove AND lv_type <> 'DOG' (cond)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "DOG_IGNORES_MEMBER" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 31: remove to_upper (func)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "LOWER_CASE_TYPE" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 55: c_breakfast 9.50 -> 8.50 (const)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "CHILD_WITH_BREAKFAST", + "MEMBER_FULL_STAY" + ] + }, + { + "object": "{{P}}HUT_FEE", + "mutant": "line 54: remove ( lv_type = 'ADULT' OR lv_type = 'CHILD' ) (cond)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "DOG_GETS_NO_BREAKFAST" + ] + } + ], + "valid": 7, + "killed": 7, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1142/reference/hut_fee.func.abap b/tasks_gen/train/G1142/reference/hut_fee.func.abap new file mode 100644 index 0000000..64521d8 --- /dev/null +++ b/tasks_gen/train/G1142/reference/hut_fee.func.abap @@ -0,0 +1,59 @@ +FUNCTION {{p}}hut_fee + IMPORTING + iv_guest_type TYPE string + iv_nights TYPE i + iv_member TYPE abap_bool + iv_breakfast TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + + CONSTANTS: c_rate_adult TYPE p LENGTH 8 DECIMALS 2 VALUE '28.00', + c_rate_child TYPE p LENGTH 8 DECIMALS 2 VALUE '14.00', + c_rate_dog TYPE p LENGTH 8 DECIMALS 2 VALUE '5.00', + c_breakfast TYPE p LENGTH 8 DECIMALS 2 VALUE '9.50', + c_member_share TYPE i VALUE 50, + c_stay_short TYPE i VALUE 3, + c_stay_long TYPE i VALUE 7, + c_share_short TYPE i VALUE 90, + c_share_long TYPE i VALUE 80. + + DATA: lv_type TYPE string, + lv_rate TYPE p LENGTH 8 DECIMALS 2, + lv_fee TYPE p LENGTH 10 DECIMALS 2. + + ev_fee = 0. + IF iv_nights < 1. + RAISE invalid_nights. + ENDIF. + + lv_type = to_upper( iv_guest_type ). + + CASE lv_type. + WHEN 'CHILD'. + lv_rate = c_rate_child. + WHEN 'DOG'. + lv_rate = c_rate_dog. + WHEN OTHERS. + lv_rate = c_rate_adult. + ENDCASE. + + IF iv_member = abap_true AND lv_type <> 'DOG'. + lv_rate = lv_rate * c_member_share / 100. + ENDIF. + + lv_fee = lv_rate * iv_nights. + + IF iv_nights >= c_stay_long. + lv_fee = lv_fee * c_share_long / 100. + ELSEIF iv_nights >= c_stay_short. + lv_fee = lv_fee * c_share_short / 100. + ENDIF. + + IF iv_breakfast = abap_true AND ( lv_type = 'ADULT' OR lv_type = 'CHILD' ). + lv_fee = lv_fee + c_breakfast * iv_nights. + ENDIF. + + ev_fee = lv_fee. +ENDFUNCTION. diff --git a/tasks_gen/train/G1142/reference/hut_fee_test.clas.abap b/tasks_gen/train/G1142/reference/hut_fee_test.clas.abap new file mode 100644 index 0000000..26b8c40 --- /dev/null +++ b/tasks_gen/train/G1142/reference/hut_fee_test.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}hut_fee_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_of + IMPORTING + iv_type TYPE string + iv_nights TYPE i + iv_member TYPE abap_bool DEFAULT abap_false + iv_breakfast TYPE abap_bool DEFAULT abap_false + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS adult_rate FOR TESTING. + METHODS child_with_breakfast FOR TESTING. + METHODS member_share FOR TESTING. + METHODS dog_rules FOR TESTING. + METHODS stay_discount FOR TESTING. + METHODS invalid_nights FOR TESTING. +ENDCLASS. + + +CLASS {{p}}hut_fee_test IMPLEMENTATION. + + METHOD fee_of. + DATA lv_subrc TYPE sysubrc. + CALL FUNCTION '{{P}}HUT_FEE' + EXPORTING + iv_guest_type = iv_type + iv_nights = iv_nights + iv_member = iv_member + iv_breakfast = iv_breakfast + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + lv_subrc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + ENDMETHOD. + + METHOD adult_rate. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '28.00' ) + act = fee_of( iv_type = `ADULT` iv_nights = 1 ) ). + ENDMETHOD. + + METHOD child_with_breakfast. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '47.00' ) + act = fee_of( iv_type = `CHILD` iv_nights = 2 iv_breakfast = abap_true ) ). + ENDMETHOD. + + METHOD member_share. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '28.00' ) + act = fee_of( iv_type = `ADULT` iv_nights = 2 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD dog_rules. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '10.00' ) + act = fee_of( iv_type = `DOG` iv_nights = 2 iv_member = abap_true iv_breakfast = abap_true ) ). + ENDMETHOD. + + METHOD stay_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '75.60' ) + act = fee_of( iv_type = `ADULT` iv_nights = 3 ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '156.80' ) + act = fee_of( iv_type = `ADULT` iv_nights = 7 ) ). + ENDMETHOD. + + METHOD invalid_nights. + DATA lv_fee TYPE decfloat34. + DATA lv_subrc TYPE sysubrc. + CALL FUNCTION '{{P}}HUT_FEE' + EXPORTING + iv_guest_type = `ADULT` + iv_nights = 0 + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_nights = 1 + OTHERS = 2. + lv_subrc = sy-subrc. + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1142/seed/hut_fee.func.abap b/tasks_gen/train/G1142/seed/hut_fee.func.abap new file mode 100644 index 0000000..17918c8 --- /dev/null +++ b/tasks_gen/train/G1142/seed/hut_fee.func.abap @@ -0,0 +1,67 @@ +FUNCTION {{p}}hut_fee + IMPORTING + iv_guest_type TYPE string + iv_nights TYPE i + iv_member TYPE abap_bool + iv_breakfast TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_nights. + +* Fee calculation of the alpine hut Alpenrose. +* Written in 2009. It works, so nobody touched it since then. + + DATA: lv_type TYPE string, + lv_rate TYPE p LENGTH 8 DECIMALS 2, + lv_room TYPE p LENGTH 10 DECIMALS 2, + lv_bf TYPE p LENGTH 10 DECIMALS 2, + lv_price TYPE p LENGTH 8 DECIMALS 2. + + ev_fee = 0. + lv_type = iv_guest_type. + TRANSLATE lv_type TO UPPER CASE. + lv_price = '9.50'. + + IF iv_nights <= 0. + RAISE invalid_nights. + ELSE. + IF lv_type = 'ADULT'. + lv_rate = '28.00'. + IF iv_member = 'X'. + lv_rate = lv_rate / 2. + ENDIF. + ELSEIF lv_type = 'CHILD'. + lv_rate = '14.00'. + IF iv_member = 'X'. + lv_rate = lv_rate / 2. + ENDIF. + ELSEIF lv_type = 'DOG'. + lv_rate = '5.00'. + ELSE. + lv_rate = '28.00'. + IF iv_member = 'X'. + lv_rate = lv_rate / 2. + ENDIF. + ENDIF. + + lv_room = lv_rate * iv_nights. + + IF iv_nights >= 7. + lv_room = lv_room * 80 / 100. + ELSEIF iv_nights >= 3. + lv_room = lv_room * 90 / 100. + ENDIF. + + lv_bf = 0. + IF iv_breakfast = 'X'. + IF lv_type = 'ADULT' OR lv_type = 'CHILD'. + lv_bf = lv_price * iv_nights. + ENDIF. + ENDIF. + + lv_room = lv_room + lv_bf. + ev_fee = lv_room. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1142/spec.md b/tasks_gen/train/G1142/spec.md new file mode 100644 index 0000000..684a521 --- /dev/null +++ b/tasks_gen/train/G1142/spec.md @@ -0,0 +1,48 @@ +# 1. Goal +Refactor the legacy function module {{P}}HUT_FEE in function group {{P}}FG_HUT. +The module calculates the fee of a stay in the alpine hut Alpenrose. The hut +keeper calls the module for each booking. The behavior of the module must stay +exactly the same. Improve the internal quality: remove the duplicated code, +reduce the nesting depth, use meaningful names, replace the magic numbers with +constants, and keep the module short and readable. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_HUT exists in package $TMP. +- Function module {{P}}HUT_FEE exists in this group. It is active and it is the + legacy implementation of the hut fee. +- The booking program of the hut calls {{P}}HUT_FEE. The call must not change. +- The legacy source is the only description of the fee rules. Read it. + +# 4. Contract +- Refactor the existing function module {{P}}HUT_FEE in function group + {{P}}FG_HUT. The name of the module stays the same. +- The interface stays exactly the same: + - importing iv_guest_type type string + - importing iv_nights type i + - importing iv_member type abap_bool + - importing iv_breakfast type abap_bool + - exporting ev_fee type decfloat34 + - exception invalid_nights +- Do not add, remove, or rename a parameter. Do not add another exception. + +# 5. Business rules +The legacy implementation of {{P}}HUT_FEE is the specification of the business +rules. The behavior must not change. Keep every rate, every discount, every +special case, and every exception of the legacy source exactly as it is, +including the rules for the guest types, for members, for breakfast, and for +long stays. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep {{P}}HUT_FEE below 40 statements. Do not + produce an ATC priority 1 or priority 2 finding. +- Out of scope: do not change function group {{P}}FG_HUT. Do not change the + public interface of {{P}}HUT_FEE. Do not add a new function module. + +# 7. Acceptance +- {{P}}HUT_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1142/task.json b/tasks_gen/train/G1142/task.json new file mode 100644 index 0000000..fe28d16 --- /dev/null +++ b/tasks_gen/train/G1142/task.json @@ -0,0 +1,85 @@ +{ + "id": "G1142", + "category": "E", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_HUT", + "description": "Function group of the alpine hut fee" + }, + { + "type": "FUNC", + "name": "{{P}}HUT_FEE", + "functionGroup": "{{P}}FG_HUT", + "file": "seed/hut_fee.func.abap", + "description": "Legacy fee module of the alpine hut" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}HUT_FEE", + "functionGroup": "{{P}}FG_HUT", + "params": [ + { + "name": "iv_guest_type", + "type": "string" + }, + { + "name": "iv_nights", + "type": "i" + }, + { + "name": "iv_member", + "type": "abap_bool" + }, + { + "name": "iv_breakfast", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}FG_HUT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}HUT_FEE_HIDDEN", + "file": "hidden/hut_fee_hidden.clas.abap", + "description": "Hidden tests for the hut fee" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}HUT_FEE", + "functionGroup": "{{P}}FG_HUT", + "file": "reference/hut_fee.func.abap", + "description": "Refactored fee module of the alpine hut" + }, + { + "type": "CLAS", + "name": "{{P}}HUT_FEE_TEST", + "file": "reference/hut_fee_test.clas.abap", + "description": "Own tests of the hut fee" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1144/faulty/m0_move_fee.func.abap b/tasks_gen/train/G1144/faulty/m0_move_fee.func.abap new file mode 100644 index 0000000..0f7cd02 --- /dev/null +++ b/tasks_gen/train/G1144/faulty/m0_move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours > 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours * lv_rate + iv_box_count * lc_box_rate. + IF iv_calc_mode = 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 2 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/faulty/m1_move_fee.func.abap b/tasks_gen/train/G1144/faulty/m1_move_fee.func.abap new file mode 100644 index 0000000..46a7892 --- /dev/null +++ b/tasks_gen/train/G1144/faulty/m1_move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours <= 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours / lv_rate + iv_box_count * lc_box_rate. + IF iv_calc_mode = 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 2 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/faulty/m2_move_fee.func.abap b/tasks_gen/train/G1144/faulty/m2_move_fee.func.abap new file mode 100644 index 0000000..9bb5b21 --- /dev/null +++ b/tasks_gen/train/G1144/faulty/m2_move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours <= 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours * lv_rate + iv_box_count / lc_box_rate. + IF iv_calc_mode = 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 2 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/faulty/m3_move_fee.func.abap b/tasks_gen/train/G1144/faulty/m3_move_fee.func.abap new file mode 100644 index 0000000..88a2289 --- /dev/null +++ b/tasks_gen/train/G1144/faulty/m3_move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours <= 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours * lv_rate + iv_box_count * lc_box_rate. + IF iv_calc_mode <> 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 2 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/faulty/m4_move_fee.func.abap b/tasks_gen/train/G1144/faulty/m4_move_fee.func.abap new file mode 100644 index 0000000..5f99211 --- /dev/null +++ b/tasks_gen/train/G1144/faulty/m4_move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours <= 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours * lv_rate + iv_box_count * lc_box_rate. + IF iv_calc_mode = 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 3 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/generation.json b/tasks_gen/train/G1144/generation.json new file mode 100644 index 0000000..cfc0d9f --- /dev/null +++ b/tasks_gen/train/G1144/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1144", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1144/hidden/move_hidden.clas.abap b/tasks_gen/train/G1144/hidden/move_hidden.clas.abap new file mode 100644 index 0000000..1ba016a --- /dev/null +++ b/tasks_gen/train/G1144/hidden/move_hidden.clas.abap @@ -0,0 +1,135 @@ +CLASS {{p}}move_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING ev_fee TYPE decfloat34 + ev_msg TYPE string + ev_subrc TYPE sysubrc. + METHODS eco_base_fee FOR TESTING. + METHODS std_base_fee FOR TESTING. + METHODS box_fee_added FOR TESTING. + METHODS exp_surcharge FOR TESTING. + METHODS rounds_half_up FOR TESTING. + METHODS rejects_empty_order FOR TESTING. + METHODS rejects_empty_date FOR TESTING. + METHODS rejects_bad_hours FOR TESTING. + METHODS rejects_negative_boxes FOR TESTING. + METHODS rejects_unknown_mode FOR TESTING. + METHODS first_error_wins FOR TESTING. +ENDCLASS. + + +CLASS {{p}}move_hidden IMPLEMENTATION. + METHOD call. + ev_fee = 0. + ev_msg = ''. + CALL FUNCTION '{{P}}MOVE_FEE' + EXPORTING + iv_order_no = iv_order_no + iv_move_date = iv_move_date + iv_work_hours = iv_work_hours + iv_box_count = iv_box_count + iv_calc_mode = iv_calc_mode + IMPORTING + ev_fee = ev_fee + ev_msg = ev_msg + EXCEPTIONS + invalid_order = 1 + invalid_date = 2 + invalid_hours = 3 + invalid_count = 4 + unknown_mode = 5 + OTHERS = 6. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD eco_base_fee. + call( EXPORTING iv_order_no = `A1` iv_move_date = '20250101' + iv_work_hours = 3 iv_box_count = 0 iv_calc_mode = `ECO` + IMPORTING ev_fee = DATA(lv_fee) ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 135 ) act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = `OK` act = lv_msg ). + ENDMETHOD. + + METHOD std_base_fee. + call( EXPORTING iv_order_no = `A2` iv_move_date = '20250201' + iv_work_hours = 2 iv_box_count = 0 iv_calc_mode = `STD` + IMPORTING ev_fee = DATA(lv_fee) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_fee ). + ENDMETHOD. + + METHOD box_fee_added. + call( EXPORTING iv_order_no = `A3` iv_move_date = '20250201' + iv_work_hours = 1 iv_box_count = 4 iv_calc_mode = `STD` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 66 ) act = lv_fee ). + ENDMETHOD. + + METHOD exp_surcharge. + call( EXPORTING iv_order_no = `A4` iv_move_date = '20250201' + iv_work_hours = 1 iv_box_count = 2 iv_calc_mode = `EXP` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '93.6' ) act = lv_fee ). + ENDMETHOD. + + METHOD rounds_half_up. + call( EXPORTING iv_order_no = `A5` iv_move_date = '20250201' + iv_work_hours = '1.005' iv_box_count = 0 iv_calc_mode = `ECO` + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.23' ) act = lv_fee ). + ENDMETHOD. + + METHOD rejects_empty_order. + call( EXPORTING iv_order_no = `` iv_move_date = '20250101' + iv_work_hours = 1 iv_box_count = 0 iv_calc_mode = `STD` + IMPORTING ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = `Order number is missing` act = lv_msg ). + ENDMETHOD. + + METHOD rejects_empty_date. + call( EXPORTING iv_order_no = `A6` iv_move_date = '00000000' + iv_work_hours = 1 iv_box_count = 0 iv_calc_mode = `STD` + IMPORTING ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = `Move date is missing` act = lv_msg ). + ENDMETHOD. + + METHOD rejects_bad_hours. + call( EXPORTING iv_order_no = `A7` iv_move_date = '20250101' + iv_work_hours = 0 iv_box_count = 0 iv_calc_mode = `STD` + IMPORTING ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = `Work hours are not positive` act = lv_msg ). + ENDMETHOD. + + METHOD rejects_negative_boxes. + call( EXPORTING iv_order_no = `A8` iv_move_date = '20250101' + iv_work_hours = 1 iv_box_count = -1 iv_calc_mode = `STD` + IMPORTING ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = `Box count is negative` act = lv_msg ). + ENDMETHOD. + + METHOD rejects_unknown_mode. + call( EXPORTING iv_order_no = `A9` iv_move_date = '20250101' + iv_work_hours = 1 iv_box_count = 0 iv_calc_mode = `FIX` + IMPORTING ev_msg = DATA(lv_msg) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = `Calculation mode is unknown` act = lv_msg ). + ENDMETHOD. + + METHOD first_error_wins. + call( EXPORTING iv_order_no = `` iv_move_date = '00000000' + iv_work_hours = -2 iv_box_count = -1 iv_calc_mode = `FIX` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1144/mutation.json b/tasks_gen/train/G1144/mutation.json new file mode 100644 index 0000000..0c98c27 --- /dev/null +++ b/tasks_gen/train/G1144/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1144", + "mutants": [ + { + "object": "{{P}}MOVE_FEE", + "mutant": "line 36: <= -> > (rel)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "BOX_FEE_ADDED", + "ECO_BASE_FEE", + "EXP_SURCHARGE", + "REJECTS_BAD_HOURS", + "REJECTS_NEGATIVE_BOXES", + "REJECTS_UNKNOWN_MODE", + "ROUNDS_HALF_UP", + "STD_BASE_FEE" + ] + }, + { + "object": "{{P}}MOVE_FEE", + "mutant": "line 58: * -> / (arith)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "BOX_FEE_ADDED", + "ECO_BASE_FEE", + "EXP_SURCHARGE", + "ROUNDS_HALF_UP", + "STD_BASE_FEE" + ] + }, + { + "object": "{{P}}MOVE_FEE", + "mutant": "line 58: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BOX_FEE_ADDED", + "EXP_SURCHARGE" + ] + }, + { + "object": "{{P}}MOVE_FEE", + "mutant": "line 59: = -> <> (eq)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "BOX_FEE_ADDED", + "ECO_BASE_FEE", + "EXP_SURCHARGE", + "ROUNDS_HALF_UP", + "STD_BASE_FEE" + ] + }, + { + "object": "{{P}}MOVE_FEE", + "mutant": "line 63: 2 -> 3 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "ROUNDS_HALF_UP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1144/reference/move_fee.func.abap b/tasks_gen/train/G1144/reference/move_fee.func.abap new file mode 100644 index 0000000..357bd8d --- /dev/null +++ b/tasks_gen/train/G1144/reference/move_fee.func.abap @@ -0,0 +1,66 @@ +FUNCTION {{p}}move_fee + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + + CONSTANTS lc_box_rate TYPE decfloat34 VALUE '1.5'. + CONSTANTS lc_exp_rate TYPE decfloat34 VALUE '1.2'. + + DATA lv_rate TYPE decfloat34. + DATA lv_subtotal TYPE decfloat34. + + ev_fee = 0. + + IF iv_order_no IS INITIAL. + ev_msg = 'Order number is missing'. + RAISE invalid_order. + ENDIF. + + IF iv_move_date IS INITIAL. + ev_msg = 'Move date is missing'. + RAISE invalid_date. + ENDIF. + + IF iv_work_hours <= 0. + ev_msg = 'Work hours are not positive'. + RAISE invalid_hours. + ENDIF. + + IF iv_box_count < 0. + ev_msg = 'Box count is negative'. + RAISE invalid_count. + ENDIF. + + CASE iv_calc_mode. + WHEN 'ECO'. + lv_rate = 45. + WHEN 'STD'. + lv_rate = 60. + WHEN 'EXP'. + lv_rate = 75. + WHEN OTHERS. + ev_msg = 'Calculation mode is unknown'. + RAISE unknown_mode. + ENDCASE. + + lv_subtotal = iv_work_hours * lv_rate + iv_box_count * lc_box_rate. + IF iv_calc_mode = 'EXP'. + lv_subtotal = lv_subtotal * lc_exp_rate. + ENDIF. + + ev_fee = round( val = lv_subtotal dec = 2 mode = cl_abap_math=>round_half_up ). + ev_msg = 'OK'. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/reference/move_test.clas.abap b/tasks_gen/train/G1144/reference/move_test.clas.abap new file mode 100644 index 0000000..bd45f2b --- /dev/null +++ b/tasks_gen/train/G1144/reference/move_test.clas.abap @@ -0,0 +1,104 @@ +CLASS {{p}}move_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS eco_fee FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS box_fee FOR TESTING. + METHODS rejects_zero_hours FOR TESTING. + METHODS first_error_wins FOR TESTING. +ENDCLASS. + + +CLASS {{p}}move_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_msg TYPE string. + CALL FUNCTION '{{P}}MOVE_FEE' + EXPORTING + iv_order_no = iv_order_no + iv_move_date = iv_move_date + iv_work_hours = iv_work_hours + iv_box_count = iv_box_count + iv_calc_mode = iv_calc_mode + IMPORTING + ev_fee = rv_fee + ev_msg = lv_msg + EXCEPTIONS + invalid_order = 1 + invalid_date = 2 + invalid_hours = 3 + invalid_count = 4 + unknown_mode = 5 + OTHERS = 6. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD eco_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 135 ) + act = fee_for( iv_order_no = `A1` iv_move_date = '20250101' + iv_work_hours = 3 iv_box_count = 0 iv_calc_mode = `ECO` ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '93.6' ) + act = fee_for( iv_order_no = `A2` iv_move_date = '20250101' + iv_work_hours = 1 iv_box_count = 2 iv_calc_mode = `EXP` ) ). + ENDMETHOD. + + METHOD box_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( 66 ) + act = fee_for( iv_order_no = `A3` iv_move_date = '20250101' + iv_work_hours = 1 iv_box_count = 4 iv_calc_mode = `STD` ) ). + ENDMETHOD. + + METHOD rejects_zero_hours. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}MOVE_FEE' + EXPORTING + iv_order_no = `A4` + iv_move_date = '20250101' + iv_work_hours = 0 + iv_box_count = 0 + iv_calc_mode = `STD` + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_order = 1 + invalid_date = 2 + invalid_hours = 3 + invalid_count = 4 + unknown_mode = 5 + OTHERS = 6. + cl_abap_unit_assert=>assert_equals( exp = 3 act = sy-subrc ). + ENDMETHOD. + + METHOD first_error_wins. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}MOVE_FEE' + EXPORTING + iv_order_no = `` + iv_move_date = '00000000' + iv_work_hours = -1 + iv_box_count = -1 + iv_calc_mode = `FIX` + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_order = 1 + invalid_date = 2 + invalid_hours = 3 + invalid_count = 4 + unknown_mode = 5 + OTHERS = 6. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1144/seed/move_sample.func.abap b/tasks_gen/train/G1144/seed/move_sample.func.abap new file mode 100644 index 0000000..1045908 --- /dev/null +++ b/tasks_gen/train/G1144/seed/move_sample.func.abap @@ -0,0 +1,18 @@ +FUNCTION {{p}}move_sample + IMPORTING + iv_order_no TYPE string + iv_move_date TYPE d + iv_work_hours TYPE decfloat34 + iv_box_count TYPE i + iv_calc_mode TYPE string + EXPORTING + ev_fee TYPE decfloat34 + ev_msg TYPE string + EXCEPTIONS + invalid_order + invalid_date + invalid_hours + invalid_count + unknown_mode. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1144/spec.md b/tasks_gen/train/G1144/spec.md new file mode 100644 index 0000000..87b9c5e --- /dev/null +++ b/tasks_gen/train/G1144/spec.md @@ -0,0 +1,58 @@ +# 1. Goal +Calculate the price of a house move for a removal company. The dispatch program +calls one function module for each move order. The function module must reject +bad input with the correct exception and must give a short message text. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_MOVE exists in package $TMP. +- Function module {{P}}MOVE_SAMPLE exists in this group. It is the interface +template for all move fee modules. Its body is empty. + +# 4. Contract +- Create the function module {{P}}MOVE_FEE in function group {{P}}FG_MOVE. +- Use exactly the interface of {{P}}MOVE_SAMPLE: the same parameter names, +kinds, types and exceptions. +- Importing parameters: iv_order_no (string), iv_move_date (date), +iv_work_hours (decfloat34), iv_box_count (integer), iv_calc_mode (string). +- Exporting parameters: ev_fee (decfloat34), ev_msg (string). +- Exceptions: invalid_order, invalid_date, invalid_hours, invalid_count, +unknown_mode. + +# 5. Business rules +1. Check the input in this order: order number, move date, work hours, box +count, calculation mode. Raise only the first exception that applies. +2. The order number is empty: raise invalid_order. +3. The move date is empty: raise invalid_date. +4. The work hours are zero or negative: raise invalid_hours. +5. The box count is negative: raise invalid_count. +6. The calculation mode is not ECO, STD or EXP: raise unknown_mode. +7. The hourly rate is 45.00 for ECO, 60.00 for STD and 75.00 for EXP. The base +fee is the work hours multiplied by the hourly rate. +8. The box fee is 1.50 for each box. +9. The subtotal is the base fee plus the box fee. +10. For mode EXP, increase the subtotal by 20 %. +11. The fee is the subtotal rounded half up to 2 decimals. +12. ev_msg is 'OK' after a successful call. If the function module raises an +exception, ev_msg holds the text of the failed check: +- invalid_order: 'Order number is missing' +- invalid_date: 'Move date is missing' +- invalid_hours: 'Work hours are not positive' +- invalid_count: 'Box count is negative' +- unknown_mode: 'Calculation mode is unknown' + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep each method below 40 statements. +- The business domain uses words that are reserved in ABAP or in ABAP SQL +(hours, mode, order, date, count, group, value). Use the parameter names of +the contract. Do not use a reserved word as the name of a parameter, a field +or a data object. +- Out of scope: do not change {{P}}MOVE_SAMPLE. + +# 7. Acceptance +- The function module {{P}}MOVE_FEE is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1144/task.json b/tasks_gen/train/G1144/task.json new file mode 100644 index 0000000..e77e643 --- /dev/null +++ b/tasks_gen/train/G1144/task.json @@ -0,0 +1,92 @@ +{ + "id": "G1144", + "category": "D", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_MOVE", + "description": "Function group for removal move pricing" + }, + { + "type": "FUNC", + "name": "{{P}}MOVE_SAMPLE", + "functionGroup": "{{P}}FG_MOVE", + "file": "seed/move_sample.func.abap", + "description": "Interface template for move fee modules" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}MOVE_FEE", + "functionGroup": "{{P}}FG_MOVE", + "params": [ + { + "name": "iv_order_no", + "type": "string" + }, + { + "name": "iv_move_date", + "type": "d" + }, + { + "name": "iv_work_hours", + "type": "decfloat34" + }, + { + "name": "iv_box_count", + "type": "i" + }, + { + "name": "iv_calc_mode", + "type": "string" + }, + { + "name": "ev_fee", + "type": "decfloat34" + }, + { + "name": "ev_msg", + "type": "string" + } + ] + } + ], + "out_of_scope": [ + "{{P}}MOVE_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}MOVE_HIDDEN", + "file": "hidden/move_hidden.clas.abap", + "description": "D14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}MOVE_FEE", + "functionGroup": "{{P}}FG_MOVE", + "file": "reference/move_fee.func.abap", + "description": "Move fee module" + }, + { + "type": "CLAS", + "name": "{{P}}MOVE_TEST", + "file": "reference/move_test.clas.abap", + "description": "D14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1145/faulty/m0_i_still_output.ddls.asddls b/tasks_gen/train/G1145/faulty/m0_i_still_output.ddls.asddls new file mode 100644 index 0000000..4bd24b1 --- /dev/null +++ b/tasks_gen/train/G1145/faulty/m0_i_still_output.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Spirit output per still' +define view entity {{P}}I_STILL_OUTPUT + as select from {{p}}runlog +{ + key still_id as still_id, + count( * ) as runs, + max( wash_litre ) as wash_l, + sum( spirit_litre ) as spirit_l, + count( distinct cask_no ) as casks, + max( run_date ) as last_run +} +where spirit_litre > 0 +group by still_id diff --git a/tasks_gen/train/G1145/faulty/m1_i_still_output.ddls.asddls b/tasks_gen/train/G1145/faulty/m1_i_still_output.ddls.asddls new file mode 100644 index 0000000..044888d --- /dev/null +++ b/tasks_gen/train/G1145/faulty/m1_i_still_output.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Spirit output per still' +define view entity {{P}}I_STILL_OUTPUT + as select from {{p}}runlog +{ + key still_id as still_id, + count( * ) as runs, + sum( wash_litre ) as wash_l, + max( spirit_litre ) as spirit_l, + count( distinct cask_no ) as casks, + max( run_date ) as last_run +} +where spirit_litre > 0 +group by still_id diff --git a/tasks_gen/train/G1145/faulty/m2_i_still_output.ddls.asddls b/tasks_gen/train/G1145/faulty/m2_i_still_output.ddls.asddls new file mode 100644 index 0000000..7a77d6c --- /dev/null +++ b/tasks_gen/train/G1145/faulty/m2_i_still_output.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Spirit output per still' +define view entity {{P}}I_STILL_OUTPUT + as select from {{p}}runlog +{ + key still_id as still_id, + count( * ) as runs, + sum( wash_litre ) as wash_l, + sum( spirit_litre ) as spirit_l, + count( distinct cask_no ) as casks, + max( run_date ) as last_run +} +where spirit_litre <= 0 +group by still_id diff --git a/tasks_gen/train/G1145/generation.json b/tasks_gen/train/G1145/generation.json new file mode 100644 index 0000000..dddd81d --- /dev/null +++ b/tasks_gen/train/G1145/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1145", + "pool": "train", + "object_type": "DDLS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0175 (similarity spec 0.73, rules 0.65, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1145/hidden/t_runlog_hidden.clas.abap b/tasks_gen/train/G1145/hidden/t_runlog_hidden.clas.abap new file mode 100644 index 0000000..40a0a50 --- /dev/null +++ b/tasks_gen/train/G1145/hidden/t_runlog_hidden.clas.abap @@ -0,0 +1,78 @@ +CLASS {{p}}t_runlog_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS runs_of_still FOR TESTING. + METHODS wash_volume FOR TESTING. + METHODS spirit_volume FOR TESTING. + METHODS distinct_casks FOR TESTING. + METHODS last_run FOR TESTING. + METHODS failed_runs_skipped FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_runlog_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_STILL_OUTPUT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_runlog TYPE STANDARD TABLE OF {{p}}runlog WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_runlog = VALUE #( + ( run_id = 'R1' still_id = 'STILL-A' run_date = '20240301' + wash_litre = '1000.0' spirit_litre = '120.0' strength_pct = '68.50' cask_no = 'C1' ) + ( run_id = 'R2' still_id = 'STILL-A' run_date = '20240315' + wash_litre = '1200.0' spirit_litre = '150.0' strength_pct = '70.00' cask_no = 'C2' ) + ( run_id = 'R3' still_id = 'STILL-A' run_date = '20240401' + wash_litre = '900.0' spirit_litre = '100.0' strength_pct = '69.00' cask_no = 'C1' ) + ( run_id = 'R4' still_id = 'STILL-B' run_date = '20240210' + wash_litre = '800.0' spirit_litre = '0.0' strength_pct = '0.00' cask_no = '' ) + ( run_id = 'R5' still_id = 'STILL-B' run_date = '20240220' + wash_litre = '700.0' spirit_litre = '90.0' strength_pct = '65.00' cask_no = 'C3' ) + ( run_id = 'R6' still_id = 'STILL-C' run_date = '20240305' + wash_litre = '500.0' spirit_litre = '0.0' strength_pct = '0.00' cask_no = '' ) ). + go_env->insert_test_data( lt_runlog ). + ENDMETHOD. + + METHOD runs_of_still. + SELECT SINGLE runs FROM {{p}}i_still_output WHERE still_id = 'STILL-A' INTO @DATA(lv_runs). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_runs ). + ENDMETHOD. + + METHOD wash_volume. + SELECT SINGLE wash_l FROM {{p}}i_still_output WHERE still_id = 'STILL-A' INTO @DATA(lv_wash). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3100.0' ) + act = CONV decfloat34( lv_wash ) ). + ENDMETHOD. + + METHOD spirit_volume. + SELECT SINGLE spirit_l FROM {{p}}i_still_output WHERE still_id = 'STILL-A' INTO @DATA(lv_spirit). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '370.0' ) + act = CONV decfloat34( lv_spirit ) ). + ENDMETHOD. + + METHOD distinct_casks. + SELECT SINGLE casks FROM {{p}}i_still_output WHERE still_id = 'STILL-A' INTO @DATA(lv_casks). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_casks ). + ENDMETHOD. + + METHOD last_run. + SELECT SINGLE last_run FROM {{p}}i_still_output WHERE still_id = 'STILL-A' INTO @DATA(lv_last). + cl_abap_unit_assert=>assert_equals( exp = CONV dats( '20240401' ) act = lv_last ). + ENDMETHOD. + + METHOD failed_runs_skipped. + SELECT COUNT(*) FROM {{p}}i_still_output INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + SELECT SINGLE runs FROM {{p}}i_still_output WHERE still_id = 'STILL-B' INTO @DATA(lv_runs). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_runs ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1145/mutation.json b/tasks_gen/train/G1145/mutation.json new file mode 100644 index 0000000..9a76682 --- /dev/null +++ b/tasks_gen/train/G1145/mutation.json @@ -0,0 +1,40 @@ +{ + "task": "G1145", + "mutants": [ + { + "object": "{{P}}I_STILL_OUTPUT", + "mutant": "line 8: sum( -> max( (agg)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "WASH_VOLUME" + ] + }, + { + "object": "{{P}}I_STILL_OUTPUT", + "mutant": "line 9: sum( -> max( (agg)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "SPIRIT_VOLUME" + ] + }, + { + "object": "{{P}}I_STILL_OUTPUT", + "mutant": "line 13: > -> <= (rel)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "DISTINCT_CASKS", + "LAST_RUN", + "RUNS_OF_STILL", + "SPIRIT_VOLUME", + "WASH_VOLUME" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1145/reference/i_still_output.ddls.asddls b/tasks_gen/train/G1145/reference/i_still_output.ddls.asddls new file mode 100644 index 0000000..1bd0f74 --- /dev/null +++ b/tasks_gen/train/G1145/reference/i_still_output.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Spirit output per still' +define view entity {{P}}I_STILL_OUTPUT + as select from {{p}}runlog +{ + key still_id as still_id, + count( * ) as runs, + sum( wash_litre ) as wash_l, + sum( spirit_litre ) as spirit_l, + count( distinct cask_no ) as casks, + max( run_date ) as last_run +} +where spirit_litre > 0 +group by still_id diff --git a/tasks_gen/train/G1145/reference/runlog_out_test.clas.abap b/tasks_gen/train/G1145/reference/runlog_out_test.clas.abap new file mode 100644 index 0000000..d97699d --- /dev/null +++ b/tasks_gen/train/G1145/reference/runlog_out_test.clas.abap @@ -0,0 +1,43 @@ +CLASS {{p}}runlog_out_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_cds_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS setup. + METHODS output_of_one_still FOR TESTING. +ENDCLASS. + + +CLASS {{p}}runlog_out_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_STILL_OUTPUT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_runlog TYPE STANDARD TABLE OF {{p}}runlog WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_runlog = VALUE #( + ( run_id = 'X1' still_id = 'STILL-X' run_date = '20240501' + wash_litre = '400.0' spirit_litre = '50.0' strength_pct = '66.00' cask_no = 'K1' ) + ( run_id = 'X2' still_id = 'STILL-X' run_date = '20240520' + wash_litre = '600.0' spirit_litre = '80.0' strength_pct = '67.00' cask_no = 'K1' ) ). + go_env->insert_test_data( lt_runlog ). + ENDMETHOD. + + METHOD output_of_one_still. + SELECT SINGLE runs, wash_l, spirit_l, casks, last_run + FROM {{p}}i_still_output WHERE still_id = 'STILL-X' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-runs ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1000.0' ) + act = CONV decfloat34( ls_row-wash_l ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '130.0' ) + act = CONV decfloat34( ls_row-spirit_l ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-casks ). + cl_abap_unit_assert=>assert_equals( exp = CONV dats( '20240520' ) act = ls_row-last_run ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1145/seed/runlog.tabl.asabap b/tasks_gen/train/G1145/seed/runlog.tabl.asabap new file mode 100644 index 0000000..a61a294 --- /dev/null +++ b/tasks_gen/train/G1145/seed/runlog.tabl.asabap @@ -0,0 +1,15 @@ +@EndUserText.label : 'Distillation run' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}runlog { + key client : abap.clnt not null; + key run_id : abap.char(10) not null; + still_id : abap.char(10); + run_date : abap.dats; + wash_litre : abap.dec(13,1); + spirit_litre : abap.dec(13,1); + strength_pct : abap.dec(5,2); + cask_no : abap.char(10); +} diff --git a/tasks_gen/train/G1145/seed/seed_runlog.clas.abap b/tasks_gen/train/G1145/seed/seed_runlog.clas.abap new file mode 100644 index 0000000..a5bc6d8 --- /dev/null +++ b/tasks_gen/train/G1145/seed/seed_runlog.clas.abap @@ -0,0 +1,30 @@ +CLASS {{p}}seed_runlog DEFINITION + PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}seed_runlog IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_runlog TYPE STANDARD TABLE OF {{p}}runlog WITH EMPTY KEY. + lt_runlog = VALUE #( + ( run_id = 'R1' still_id = 'STILL-A' run_date = '20240301' + wash_litre = '1000.0' spirit_litre = '120.0' strength_pct = '68.50' cask_no = 'C1' ) + ( run_id = 'R2' still_id = 'STILL-A' run_date = '20240315' + wash_litre = '1200.0' spirit_litre = '150.0' strength_pct = '70.00' cask_no = 'C2' ) + ( run_id = 'R3' still_id = 'STILL-A' run_date = '20240401' + wash_litre = '900.0' spirit_litre = '100.0' strength_pct = '69.00' cask_no = 'C1' ) + ( run_id = 'R4' still_id = 'STILL-B' run_date = '20240210' + wash_litre = '800.0' spirit_litre = '0.0' strength_pct = '0.00' cask_no = '' ) + ( run_id = 'R5' still_id = 'STILL-B' run_date = '20240220' + wash_litre = '700.0' spirit_litre = '90.0' strength_pct = '65.00' cask_no = 'C3' ) + ( run_id = 'R6' still_id = 'STILL-C' run_date = '20240305' + wash_litre = '500.0' spirit_litre = '0.0' strength_pct = '0.00' cask_no = '' ) ). + DELETE FROM {{p}}runlog. + INSERT {{p}}runlog FROM TABLE @lt_runlog. + out->write( |Runs: { lines( lt_runlog ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1145/spec.md b/tasks_gen/train/G1145/spec.md new file mode 100644 index 0000000..b8214b5 --- /dev/null +++ b/tasks_gen/train/G1145/spec.md @@ -0,0 +1,42 @@ +# 1. Goal +Show the spirit output of each still of a whisky distillery. A report and an +OData service read the output, so the calculation must be in the database, in a +CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}RUNLOG is in package $TMP. The table is active and contains data. + Read the definition of the table in the system. The table has one row for each + distillation run. Its columns contain the still, the run date, the volume of + the wash in litres, the volume of the spirit in litres, the strength of the + spirit in percent, and the number of the cask that the run filled. The table + has more columns that you do not need. +- Do not change the table. + +# 4. Contract +- Create the CDS view entity {{P}}I_STILL_OUTPUT in package $TMP. +- The view has these elements, with these names: STILL_ID (key), RUNS, WASH_L, + SPIRIT_L, CASKS, LAST_RUN. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view has one row for each still that has at least one counted run. +2. A run with a spirit volume of zero or less is a failed run. Do not count a + failed run. A still that has only failed runs has no row in the view. +3. RUNS is the number of counted runs of the still. +4. WASH_L is the sum of the wash volumes of the counted runs, in litres. +5. SPIRIT_L is the sum of the spirit volumes of the counted runs, in litres. +6. CASKS is the number of different casks that the counted runs filled. +7. LAST_RUN is the latest run date of the counted runs. + +# 6. Constraints +- Release target: SAP_BASIS 8.16 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep methods below 40 statements. +- Out of scope: do not change {{P}}RUNLOG. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1145/task.json b/tasks_gen/train/G1145/task.json new file mode 100644 index 0000000..52aca74 --- /dev/null +++ b/tasks_gen/train/G1145/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1145", + "category": "F", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}RUNLOG", + "file": "seed/runlog.tabl.asabap", + "description": "Distillation run of a distillery" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_RUNLOG", + "file": "seed/seed_runlog.clas.abap", + "run": true, + "description": "Seed data for {{P}}RUNLOG" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_STILL_OUTPUT", + "fields": [ + "STILL_ID", + "RUNS", + "WASH_L", + "SPIRIT_L", + "CASKS", + "LAST_RUN" + ] + } + ], + "out_of_scope": [ + "{{P}}RUNLOG" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_RUNLOG_HIDDEN", + "file": "hidden/t_runlog_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_STILL_OUTPUT", + "file": "reference/i_still_output.ddls.asddls", + "description": "Spirit output per still" + }, + { + "type": "CLAS", + "name": "{{P}}RUNLOG_OUT_TEST", + "file": "reference/runlog_out_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1147/generation.json b/tasks_gen/train/G1147/generation.json new file mode 100644 index 0000000..80b24e9 --- /dev/null +++ b/tasks_gen/train/G1147/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1147", + "pool": "train", + "object_type": "PROG", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1147/reference/charge_stat.prog.abap b/tasks_gen/train/G1147/reference/charge_stat.prog.abap new file mode 100644 index 0000000..a544541 --- /dev/null +++ b/tasks_gen/train/G1147/reference/charge_stat.prog.abap @@ -0,0 +1,85 @@ +REPORT {{p}}charge_stat. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + station TYPE {{p}}charge-station, + total_kwh TYPE {{p}}charge-energy_kwh, + sessions TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + METHODS select_totals + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_totals. + SELECT station, SUM( energy_kwh ) AS total_kwh, COUNT( * ) AS sessions + FROM {{p}}charge + WHERE session_date >= @iv_from + AND session_date <= @iv_to + GROUP BY station + ORDER BY total_kwh DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_totals( iv_from = p_from iv_to = p_to ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_station FOR TESTING. + METHODS date_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}CHARGE' ) ) ). + DATA lt_charge TYPE STANDARD TABLE OF {{p}}charge WITH EMPTY KEY. + lt_charge = VALUE #( ( session_id = 1 station = 'A' session_date = '20260101' energy_kwh = 10 ) + ( session_id = 2 station = 'A' session_date = '20260102' energy_kwh = 5 ) + ( session_id = 3 station = 'B' session_date = '20260101' energy_kwh = 40 ) ). + go_env->insert_test_data( lt_charge ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_station. + DATA(lt_lines) = NEW lcl_report( )->select_totals( iv_from = '20260101' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-station ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-sessions ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_report( )->select_totals( iv_from = '20260102' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}charge-energy_kwh( 5 ) act = lt_lines[ 1 ]-total_kwh ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1147/seed/charge.tabl.asabap b/tasks_gen/train/G1147/seed/charge.tabl.asabap new file mode 100644 index 0000000..546438c --- /dev/null +++ b/tasks_gen/train/G1147/seed/charge.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Charging sessions' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}charge { + key client : abap.clnt not null; + key session_id : abap.int4 not null; + station : abap.char(10); + session_date : abap.dats; + energy_kwh : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1147/seed/charge_seed.clas.abap b/tasks_gen/train/G1147/seed/charge_seed.clas.abap new file mode 100644 index 0000000..62a2a03 --- /dev/null +++ b/tasks_gen/train/G1147/seed/charge_seed.clas.abap @@ -0,0 +1,17 @@ +CLASS {{p}}charge_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}charge_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}charge. + INSERT {{p}}charge FROM TABLE @( VALUE #( + ( session_id = 1 station = 'ST01' session_date = '20260110' energy_kwh = '120.00' ) + ( session_id = 2 station = 'ST01' session_date = '20260210' energy_kwh = '80.00' ) + ( session_id = 3 station = 'ST02' session_date = '20260115' energy_kwh = '300.00' ) + ( session_id = 4 station = 'ST03' session_date = '20251231' energy_kwh = '500.00' ) + ( session_id = 5 station = 'ST03' session_date = '20260301' energy_kwh = '20.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1147/spec.md b/tasks_gen/train/G1147/spec.md new file mode 100644 index 0000000..0074375 --- /dev/null +++ b/tasks_gen/train/G1147/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +Show the energy that each charging station of a charging network delivered in a +period in an ALV list. The network operator uses the list to compare the +stations. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}CHARGE exists in package $TMP. It contains data. + Fields: SESSION_ID (INT4, key), STATION (CHAR 10), SESSION_DATE (DATS), + ENERGY_KWH (DEC 15,2). + +# 4. Contract +- Create the executable program {{P}}CHARGE_STAT in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first session date to include. + - Parameter P_TO, type DATS, mandatory: the last session date to include. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: STATION, TOTAL_KWH, SESSIONS. + +# 5. Business rules +1. Include a session only if SESSION_DATE is on or after P_FROM and on or before + P_TO. +2. Show one line for each station that has at least one included session. +3. TOTAL_KWH is the sum of ENERGY_KWH of the included sessions of the station. + SESSIONS is the number of included sessions of the station. +4. Sort the lines by TOTAL_KWH, the highest total first. +5. Sort the lines by TOTAL_KWH, the lowest total first. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}CHARGE. + +# 7. Acceptance +- The program is active and has no syntax error. +- The list shows one line for each station that has at least one included + session. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1147/task.json b/tasks_gen/train/G1147/task.json new file mode 100644 index 0000000..ad2fd31 --- /dev/null +++ b/tasks_gen/train/G1147/task.json @@ -0,0 +1,73 @@ +{ + "id": "G1147", + "category": "H", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "Business rules 4 and 5 contradict each other: rule 4 sorts the lines by TOTAL_KWH with the highest total first, rule 5 sorts them by TOTAL_KWH with the lowest total first.", + "keywords": [ + [ + "sort", + "order" + ], + [ + "contradict", + "rule" + ], + [ + "conflict", + "rule" + ], + [ + "total", + "rule" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}CHARGE", + "file": "seed/charge.tabl.asabap", + "description": "Charging sessions of a charging network" + }, + { + "type": "CLAS", + "name": "{{P}}CHARGE_SEED", + "file": "seed/charge_seed.clas.abap", + "description": "Seed data for {{P}}CHARGE", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}CHARGE_STAT", + "parameters": [ + "p_from", + "p_to" + ] + } + ], + "out_of_scope": [ + "{{P}}CHARGE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "PROG", + "name": "{{P}}CHARGE_STAT", + "file": "reference/charge_stat.prog.abap", + "description": "Energy per charging station (reference assumes the highest total first)" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1148/faulty/m0_studio_bill.prog.abap b/tasks_gen/train/G1148/faulty/m0_studio_bill.prog.abap new file mode 100644 index 0000000..f1538b1 --- /dev/null +++ b/tasks_gen/train/G1148/faulty/m0_studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date < @iv_from + AND tracks > 0 + AND customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) >= @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/faulty/m1_studio_bill.prog.abap b/tasks_gen/train/G1148/faulty/m1_studio_bill.prog.abap new file mode 100644 index 0000000..c011cb8 --- /dev/null +++ b/tasks_gen/train/G1148/faulty/m1_studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date >= @iv_from + OR tracks > 0 + AND customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) >= @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/faulty/m2_studio_bill.prog.abap b/tasks_gen/train/G1148/faulty/m2_studio_bill.prog.abap new file mode 100644 index 0000000..7f92e50 --- /dev/null +++ b/tasks_gen/train/G1148/faulty/m2_studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date >= @iv_from + AND tracks <= 0 + AND customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) >= @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/faulty/m3_studio_bill.prog.abap b/tasks_gen/train/G1148/faulty/m3_studio_bill.prog.abap new file mode 100644 index 0000000..8f7754b --- /dev/null +++ b/tasks_gen/train/G1148/faulty/m3_studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date >= @iv_from + AND tracks > 0 + OR customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) >= @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/faulty/m4_studio_bill.prog.abap b/tasks_gen/train/G1148/faulty/m4_studio_bill.prog.abap new file mode 100644 index 0000000..f019014 --- /dev/null +++ b/tasks_gen/train/G1148/faulty/m4_studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date >= @iv_from + AND tracks > 0 + AND customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) < @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/generation.json b/tasks_gen/train/G1148/generation.json new file mode 100644 index 0000000..23bee55 --- /dev/null +++ b/tasks_gen/train/G1148/generation.json @@ -0,0 +1,44 @@ +{ + "id": "G1148", + "pool": "train", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t15_hidden.clas.abap line 81: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 115: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 128: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 130: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"" + ], + "by_check": { + "abaplint": 12 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1148/hidden/t15_hidden.clas.abap b/tasks_gen/train/G1148/hidden/t15_hidden.clas.abap new file mode 100644 index 0000000..ea19c28 --- /dev/null +++ b/tasks_gen/train/G1148/hidden/t15_hidden.clas.abap @@ -0,0 +1,132 @@ +CLASS {{p}}t15_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_min TYPE p LENGTH 15 DECIMALS 2. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS run + IMPORTING iv_from TYPE d + iv_min TYPE ty_min + it_customer TYPE tt_customer OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + + METHODS all_customers FOR TESTING. + METHODS sorted_by_fee FOR TESTING. + METHODS totals_and_counts FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS customer_filter FOR TESTING. + METHODS zero_tracks_excluded FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t15_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false + metadata = abap_false + data = abap_true ). + SUBMIT {{p}}studio_bill + WITH p_from = iv_from + WITH p_min = iv_min + WITH s_cust IN it_customer + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_customers. + cl_abap_unit_assert=>assert_equals( + exp = 3 + act = lines_of( run( iv_from = '20260101' iv_min = 1 ) ) ). + ENDMETHOD. + + METHOD sorted_by_fee. + DATA(lr_data) = run( iv_from = '20260101' iv_min = 1 ). + cl_abap_unit_assert=>assert_equals( + exp = `BLUE` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CUSTOMER` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `DELTA` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `CUSTOMER` ) ). + ENDMETHOD. + + METHOD totals_and_counts. + DATA(lr_data) = run( iv_from = '20260101' iv_min = 1 ). + cl_abap_unit_assert=>assert_equals( + exp = `ACME` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `CUSTOMER` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `2` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `SESSIONS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `18` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TRACKS` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `450.00` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FEE` ) ). + ENDMETHOD. + + METHOD date_filter. + cl_abap_unit_assert=>assert_equals( + exp = 1 + act = lines_of( run( iv_from = '20260301' iv_min = 1 ) ) ). + ENDMETHOD. + + METHOD customer_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_min = 1 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'ACME' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `450.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FEE` ) ). + ENDMETHOD. + + METHOD zero_tracks_excluded. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = lines_of( run( iv_from = '20260101' iv_min = 1 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'CORAL' ) ) ) ) ). + ENDMETHOD. + + METHOD min_fee_filter. + cl_abap_unit_assert=>assert_equals( + exp = 2 + act = lines_of( run( iv_from = '20260101' iv_min = 300 ) ) ). + cl_abap_unit_assert=>assert_equals( + exp = 2 + act = lines_of( run( iv_from = '20260101' iv_min = 450 ) ) ). + cl_abap_unit_assert=>assert_equals( + exp = 1 + act = lines_of( run( iv_from = '20260101' iv_min = 460 ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/mutation.json b/tasks_gen/train/G1148/mutation.json new file mode 100644 index 0000000..e7e3edd --- /dev/null +++ b/tasks_gen/train/G1148/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1148", + "mutants": [ + { + "object": "{{P}}STUDIO_BILL", + "mutant": "line 40: >= -> < (rel)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "ALL_CUSTOMERS", + "CUSTOMER_FILTER", + "DATE_FILTER", + "MIN_FEE_FILTER", + "SORTED_BY_FEE", + "TOTALS_AND_COUNTS" + ] + }, + { + "object": "{{P}}STUDIO_BILL", + "mutant": "line 41: AND -> OR (logic)", + "status": "killed", + "hidden": "4/7", + "failed_tests": [ + "CUSTOMER_FILTER", + "DATE_FILTER", + "ZERO_TRACKS_EXCLUDED" + ] + }, + { + "object": "{{P}}STUDIO_BILL", + "mutant": "line 41: > -> <= (rel)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "ALL_CUSTOMERS", + "CUSTOMER_FILTER", + "DATE_FILTER", + "MIN_FEE_FILTER", + "SORTED_BY_FEE", + "TOTALS_AND_COUNTS" + ] + }, + { + "object": "{{P}}STUDIO_BILL", + "mutant": "line 42: AND -> OR (logic)", + "status": "killed", + "hidden": "4/7", + "failed_tests": [ + "CUSTOMER_FILTER", + "DATE_FILTER", + "ZERO_TRACKS_EXCLUDED" + ] + }, + { + "object": "{{P}}STUDIO_BILL", + "mutant": "line 44: >= -> < (rel)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "ALL_CUSTOMERS", + "CUSTOMER_FILTER", + "DATE_FILTER", + "MIN_FEE_FILTER", + "SORTED_BY_FEE", + "TOTALS_AND_COUNTS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1148/reference/studio_bill.prog.abap b/tasks_gen/train/G1148/reference/studio_bill.prog.abap new file mode 100644 index 0000000..f3ca097 --- /dev/null +++ b/tasks_gen/train/G1148/reference/studio_bill.prog.abap @@ -0,0 +1,122 @@ +REPORT {{p}}studio_bill. + +TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2. + +DATA gv_customer TYPE {{p}}studio-customer. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_min TYPE ty_amount OBLIGATORY. +SELECT-OPTIONS s_cust FOR gv_customer. + +CLASS lcl_billing DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + customer TYPE {{p}}studio-customer, + sessions TYPE i, + tracks TYPE i, + fee TYPE {{p}}studio-fee, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_customer TYPE RANGE OF {{p}}studio-customer. + + METHODS select_bills + IMPORTING iv_from TYPE d + iv_min TYPE ty_amount + it_customer TYPE tt_customer + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_billing IMPLEMENTATION. + METHOD select_bills. + SELECT customer, + COUNT( * ) AS sessions, + SUM( tracks ) AS tracks, + SUM( fee ) AS fee + FROM {{p}}studio + WHERE rec_date >= @iv_from + AND tracks > 0 + AND customer IN @it_customer + GROUP BY customer + HAVING SUM( fee ) >= @iv_min + ORDER BY fee DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_lines. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_billing) = NEW lcl_billing( ). + DATA(gt_lines) = go_billing->select_bills( iv_from = p_from + iv_min = p_min + it_customer = s_cust[] ). + go_billing->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_billing DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_customer FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS zero_tracks_ignored FOR TESTING. + METHODS min_fee_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_billing IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}STUDIO' ) ) ). + DATA lt_studio TYPE STANDARD TABLE OF {{p}}studio WITH EMPTY KEY. + lt_studio = VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260101' tracks = 10 fee = 100 ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260102' tracks = 5 fee = 50 ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260101' tracks = 0 fee = 0 ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251201' tracks = 7 fee = 70 ) ). + go_env->insert_test_data( lt_studio ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_customer. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sessions ). + cl_abap_unit_assert=>assert_equals( exp = 15 act = lt_lines[ 1 ]-tracks ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}studio-fee( 150 ) act = lt_lines[ 1 ]-fee ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 0 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD zero_tracks_ignored. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20260101' iv_min = 0 + it_customer = VALUE #( ( sign = 'I' option = 'EQ' low = 'BLUE' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_lines ) ). + ENDMETHOD. + + METHOD min_fee_filter. + DATA(lt_lines) = NEW lcl_billing( )->select_bills( + iv_from = '20251201' iv_min = 120 it_customer = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ACME' act = lt_lines[ 1 ]-customer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/seed/studio.tabl.asabap b/tasks_gen/train/G1148/seed/studio.tabl.asabap new file mode 100644 index 0000000..3ede8c6 --- /dev/null +++ b/tasks_gen/train/G1148/seed/studio.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Recording sessions of the studio' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}studio { + key client : abap.clnt not null; + key rec_id : abap.int4 not null; + customer : abap.char(10); + rec_date : abap.dats; + tracks : abap.int4; + fee : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1148/seed/t15_seed.clas.abap b/tasks_gen/train/G1148/seed/t15_seed.clas.abap new file mode 100644 index 0000000..d3b3f2f --- /dev/null +++ b/tasks_gen/train/G1148/seed/t15_seed.clas.abap @@ -0,0 +1,18 @@ +CLASS {{p}}t15_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}t15_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}studio. + INSERT {{p}}studio FROM TABLE @( VALUE #( + ( rec_id = 1 customer = 'ACME' rec_date = '20260110' tracks = 12 fee = '300.00' ) + ( rec_id = 2 customer = 'ACME' rec_date = '20260205' tracks = 6 fee = '150.00' ) + ( rec_id = 3 customer = 'BLUE' rec_date = '20260115' tracks = 9 fee = '500.00' ) + ( rec_id = 4 customer = 'BLUE' rec_date = '20251220' tracks = 3 fee = '100.00' ) + ( rec_id = 5 customer = 'CORAL' rec_date = '20260301' tracks = 0 fee = '0.00' ) + ( rec_id = 6 customer = 'DELTA' rec_date = '20260401' tracks = 5 fee = '225.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1148/spec.md b/tasks_gen/train/G1148/spec.md new file mode 100644 index 0000000..107c6ab --- /dev/null +++ b/tasks_gen/train/G1148/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +Show the studio fee of each customer in an ALV list. The studio uses the list to +bill its customers for a period. + +# 2. Open questions +None. + +# 3. Context +- The table {{P}}STUDIO exists in package $TMP. It contains the recording + sessions of the studio and it contains data. + Fields: REC_ID (INT4, key), CUSTOMER (CHAR 10), REC_DATE (DATS), + TRACKS (INT4), FEE (DEC 15,2). + +# 4. Contract +- Create the executable program {{P}}STUDIO_BILL in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first recording date to include. + - Parameter P_MIN, type P LENGTH 15 DECIMALS 2, mandatory: the minimum total + fee. + - Select-option S_CUST for the customer. +- Show the result with CL_SALV_TABLE. The output table has these columns with + these names: + - CUSTOMER: the customer. + - SESSIONS: the number of included recording sessions of the customer. + - TRACKS: the sum of TRACKS of the included sessions. + - FEE: the sum of FEE of the included sessions. + +# 5. Business rules +1. Include a recording session only if REC_DATE is on or after P_FROM, CUSTOMER + is in S_CUST, and TRACKS is greater than zero. +2. Show one line for each customer that has at least one included session. +3. Show only the customers whose total fee is on or after P_MIN. +4. Sort the lines by FEE, the highest fee first. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change the table {{P}}STUDIO. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1148/task.json b/tasks_gen/train/G1148/task.json new file mode 100644 index 0000000..78a64fb --- /dev/null +++ b/tasks_gen/train/G1148/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1148", + "category": "B", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}STUDIO", + "file": "seed/studio.tabl.asabap", + "description": "Recording sessions of the studio" + }, + { + "type": "CLAS", + "name": "{{P}}T15_SEED", + "file": "seed/t15_seed.clas.abap", + "description": "T15 seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}STUDIO_BILL", + "parameters": [ + "p_from", + "p_min", + "s_cust" + ] + } + ], + "out_of_scope": [ + "{{P}}STUDIO" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T15_HIDDEN", + "file": "hidden/t15_hidden.clas.abap", + "description": "T15 hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}STUDIO_BILL", + "file": "reference/studio_bill.prog.abap", + "description": "Studio fee per customer" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1150/faulty/m0_farrier_fee.func.abap b/tasks_gen/train/G1150/faulty/m0_farrier_fee.func.abap new file mode 100644 index 0000000..385a5a7 --- /dev/null +++ b/tasks_gen/train/G1150/faulty/m0_farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses >= 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_true. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes = abap_true. + ev_fee = ev_fee + iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee * c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/faulty/m1_farrier_fee.func.abap b/tasks_gen/train/G1150/faulty/m1_farrier_fee.func.abap new file mode 100644 index 0000000..cd4e839 --- /dev/null +++ b/tasks_gen/train/G1150/faulty/m1_farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses < 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_false. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes = abap_true. + ev_fee = ev_fee + iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee * c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/faulty/m2_farrier_fee.func.abap b/tasks_gen/train/G1150/faulty/m2_farrier_fee.func.abap new file mode 100644 index 0000000..66a924b --- /dev/null +++ b/tasks_gen/train/G1150/faulty/m2_farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses < 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_true. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes <> abap_true. + ev_fee = ev_fee + iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee * c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/faulty/m3_farrier_fee.func.abap b/tasks_gen/train/G1150/faulty/m3_farrier_fee.func.abap new file mode 100644 index 0000000..24eb5e8 --- /dev/null +++ b/tasks_gen/train/G1150/faulty/m3_farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses < 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_true. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes = abap_true. + ev_fee = ev_fee - iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee * c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/faulty/m4_farrier_fee.func.abap b/tasks_gen/train/G1150/faulty/m4_farrier_fee.func.abap new file mode 100644 index 0000000..900c828 --- /dev/null +++ b/tasks_gen/train/G1150/faulty/m4_farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses < 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_true. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes = abap_true. + ev_fee = ev_fee + iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee / c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/generation.json b/tasks_gen/train/G1150/generation.json new file mode 100644 index 0000000..279109f --- /dev/null +++ b/tasks_gen/train/G1150/generation.json @@ -0,0 +1,65 @@ +{ + "id": "G1150", + "pool": "train", + "object_type": "FUNC", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t14_test.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/t14_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/t14_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"1\"", + "reference/t14_test.clas.abap line 62: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/t14_test.clas.abap line 62: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t14_hidden.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t14_hidden.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"1\"", + "hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"6\"", + "hidden/t14_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "Too close to task G0144 (similarity spec 0.72, rules 0.67, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 15, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G0144 (similarity spec 0.77, rules 0.72, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/farrier_test.clas.abap line 75: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_visit\"", + "reference/farrier_test.clas.abap line 75: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/farrier_hidden.clas.abap line 138: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_visit\"", + "hidden/farrier_hidden.clas.abap line 138: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1150/hidden/farrier_hidden.clas.abap b/tasks_gen/train/G1150/hidden/farrier_hidden.clas.abap new file mode 100644 index 0000000..f176741 --- /dev/null +++ b/tasks_gen/train/G1150/hidden/farrier_hidden.clas.abap @@ -0,0 +1,169 @@ +CLASS {{p}}farrier_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS run_visit + IMPORTING iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + ev_subrc TYPE sysubrc. + METHODS negative_horses FOR TESTING. + METHODS zero_horses_rejected FOR TESTING. + METHODS standard_visit FOR TESTING. + METHODS four_horses_standard FOR TESTING. + METHODS bulk_rate_from_fifth FOR TESTING. + METHODS new_shoes_charge FOR TESTING. + METHODS travel_minimum FOR TESTING. + METHODS travel_rounded_up FOR TESTING. + METHODS contract_discount FOR TESTING. + METHODS contract_and_shoes FOR TESTING. +ENDCLASS. + + +CLASS {{p}}farrier_hidden IMPLEMENTATION. + METHOD run_visit. + CALL FUNCTION '{{P}}FARRIER_FEE' + EXPORTING + iv_horses = iv_horses + iv_km = iv_km + iv_new_shoes = iv_new_shoes + iv_contract = iv_contract + IMPORTING + ev_fee = ev_fee + ev_weeks = ev_weeks + ev_reject = ev_reject + EXCEPTIONS + invalid_horses = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD negative_horses. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + run_visit( EXPORTING iv_horses = -1 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD zero_horses_rejected. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '0'. + run_visit( EXPORTING iv_horses = 0 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_true( lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_weeks ). + ENDMETHOD. + + METHOD standard_visit. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '100'. + run_visit( EXPORTING iv_horses = 2 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_false( lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_weeks ). + ENDMETHOD. + + METHOD four_horses_standard. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '190'. + run_visit( EXPORTING iv_horses = 4 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + ENDMETHOD. + + METHOD bulk_rate_from_fifth. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '230'. + run_visit( EXPORTING iv_horses = 5 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_weeks ). + ENDMETHOD. + + METHOD new_shoes_charge. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '160'. + run_visit( EXPORTING iv_horses = 2 iv_km = 10 iv_new_shoes = abap_true iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_weeks ). + ENDMETHOD. + + METHOD travel_minimum. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '55'. + run_visit( EXPORTING iv_horses = 1 iv_km = 3 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + ENDMETHOD. + + METHOD travel_rounded_up. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '61'. + run_visit( EXPORTING iv_horses = 1 iv_km = 26 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + ENDMETHOD. + + METHOD contract_discount. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '88'. + run_visit( EXPORTING iv_horses = 2 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_true + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_weeks ). + ENDMETHOD. + + METHOD contract_and_shoes. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '352'. + run_visit( EXPORTING iv_horses = 5 iv_km = 50 iv_new_shoes = abap_true iv_contract = abap_true + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_weeks ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1150/mutation.json b/tasks_gen/train/G1150/mutation.json new file mode 100644 index 0000000..7113717 --- /dev/null +++ b/tasks_gen/train/G1150/mutation.json @@ -0,0 +1,76 @@ +{ + "task": "G1150", + "mutants": [ + { + "object": "{{P}}FARRIER_FEE", + "mutant": "line 25: < -> >= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "BULK_RATE_FROM_FIFTH", + "CONTRACT_AND_SHOES", + "CONTRACT_DISCOUNT", + "FOUR_HORSES_STANDARD", + "NEGATIVE_HORSES", + "NEW_SHOES_CHARGE", + "STANDARD_VISIT", + "TRAVEL_MINIMUM", + "TRAVEL_ROUNDED_UP", + "ZERO_HORSES_REJECTED" + ] + }, + { + "object": "{{P}}FARRIER_FEE", + "mutant": "line 40: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "BULK_RATE_FROM_FIFTH", + "CONTRACT_AND_SHOES", + "CONTRACT_DISCOUNT", + "NEW_SHOES_CHARGE", + "STANDARD_VISIT" + ] + }, + { + "object": "{{P}}FARRIER_FEE", + "mutant": "line 54: = -> <> (eq)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "BULK_RATE_FROM_FIFTH", + "CONTRACT_AND_SHOES", + "CONTRACT_DISCOUNT", + "FOUR_HORSES_STANDARD", + "NEW_SHOES_CHARGE", + "STANDARD_VISIT", + "TRAVEL_MINIMUM", + "TRAVEL_ROUNDED_UP" + ] + }, + { + "object": "{{P}}FARRIER_FEE", + "mutant": "line 55: + -> - (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "CONTRACT_AND_SHOES", + "NEW_SHOES_CHARGE" + ] + }, + { + "object": "{{P}}FARRIER_FEE", + "mutant": "line 68: * -> / (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "CONTRACT_AND_SHOES", + "CONTRACT_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1150/reference/farrier_fee.func.abap b/tasks_gen/train/G1150/reference/farrier_fee.func.abap new file mode 100644 index 0000000..e8c8b15 --- /dev/null +++ b/tasks_gen/train/G1150/reference/farrier_fee.func.abap @@ -0,0 +1,71 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + + CONSTANTS c_shoeing_rate TYPE decfloat34 VALUE '45'. + CONSTANTS c_bulk_rate TYPE decfloat34 VALUE '40'. + CONSTANTS c_bulk_from TYPE i VALUE 5. + CONSTANTS c_new_shoes_rate TYPE decfloat34 VALUE '30'. + CONSTANTS c_km_rate TYPE decfloat34 VALUE '0.6'. + CONSTANTS c_travel_minimum TYPE decfloat34 VALUE '10'. + CONSTANTS c_contract_percent TYPE decfloat34 VALUE '88'. + CONSTANTS c_full_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_weeks_standard TYPE i VALUE 6. + CONSTANTS c_weeks_contract TYPE i VALUE 4. + + IF iv_horses < 0. + RAISE invalid_horses. + ENDIF. + + IF iv_horses = 0. + ev_reject = abap_true. + ev_fee = 0. + ev_weeks = 0. + RETURN. + ENDIF. + + ev_reject = abap_false. + +* Next visit: every six weeks, contract stables every four weeks. + ev_weeks = c_weeks_standard. + IF iv_contract = abap_true. + ev_weeks = c_weeks_contract. + ENDIF. + +* Shoeing fee: the first four horses cost the standard rate, every further +* horse costs the bulk rate. + DATA(lv_bulk_horses) = iv_horses - c_bulk_from + 1. + IF lv_bulk_horses < 0. + lv_bulk_horses = 0. + ENDIF. + DATA(lv_standard_horses) = iv_horses - lv_bulk_horses. + ev_fee = lv_standard_horses * c_shoeing_rate + lv_bulk_horses * c_bulk_rate. + +* New shoes are charged for every horse. + IF iv_new_shoes = abap_true. + ev_fee = ev_fee + iv_horses * c_new_shoes_rate. + ENDIF. + +* Travel: a rate per kilometre, at least the minimum, rounded up to a whole +* euro. + DATA(lv_travel) = iv_km * c_km_rate. + IF lv_travel < c_travel_minimum. + lv_travel = c_travel_minimum. + ENDIF. + ev_fee = ev_fee + ceil( lv_travel ). + +* Contract stables get a discount on the whole visit. + IF iv_contract = abap_true. + ev_fee = ev_fee * c_contract_percent / c_full_percent. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/reference/farrier_test.clas.abap b/tasks_gen/train/G1150/reference/farrier_test.clas.abap new file mode 100644 index 0000000..ea9dc62 --- /dev/null +++ b/tasks_gen/train/G1150/reference/farrier_test.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}farrier_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS run_visit + IMPORTING iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + ev_subrc TYPE sysubrc. + METHODS standard_visit FOR TESTING. + METHODS bulk_visit FOR TESTING. + METHODS travel_rules FOR TESTING. + METHODS contract_visit FOR TESTING. + METHODS rejected_and_invalid FOR TESTING. +ENDCLASS. + + +CLASS {{p}}farrier_test IMPLEMENTATION. + METHOD run_visit. + CALL FUNCTION '{{P}}FARRIER_FEE' + EXPORTING + iv_horses = iv_horses + iv_km = iv_km + iv_new_shoes = iv_new_shoes + iv_contract = iv_contract + IMPORTING + ev_fee = ev_fee + ev_weeks = ev_weeks + ev_reject = ev_reject + EXCEPTIONS + invalid_horses = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD standard_visit. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '100'. + run_visit( EXPORTING iv_horses = 2 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_false( lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_weeks ). + ENDMETHOD. + + METHOD bulk_visit. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '230'. + run_visit( EXPORTING iv_horses = 5 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_weeks ). + ENDMETHOD. + + METHOD travel_rules. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_minimum TYPE decfloat34 VALUE '55'. + DATA lv_rounded TYPE decfloat34 VALUE '61'. + run_visit( EXPORTING iv_horses = 1 iv_km = 3 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_minimum act = lv_fee ). + run_visit( EXPORTING iv_horses = 1 iv_km = 26 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_rounded act = lv_fee ). + ENDMETHOD. + + METHOD contract_visit. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '352'. + run_visit( EXPORTING iv_horses = 5 iv_km = 50 iv_new_shoes = abap_true iv_contract = abap_true + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_weeks ). + ENDMETHOD. + + METHOD rejected_and_invalid. + DATA lv_fee TYPE decfloat34. + DATA lv_weeks TYPE i. + DATA lv_reject TYPE abap_bool. + DATA lv_subrc TYPE sysubrc. + DATA lv_expected TYPE decfloat34 VALUE '0'. + run_visit( EXPORTING iv_horses = 0 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_true( lv_reject ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_weeks ). + run_visit( EXPORTING iv_horses = -1 iv_km = 10 iv_new_shoes = abap_false iv_contract = abap_false + IMPORTING ev_fee = lv_fee ev_weeks = lv_weeks ev_reject = lv_reject ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1150/seed/farrier_fee.func.abap b/tasks_gen/train/G1150/seed/farrier_fee.func.abap new file mode 100644 index 0000000..06c73ca --- /dev/null +++ b/tasks_gen/train/G1150/seed/farrier_fee.func.abap @@ -0,0 +1,64 @@ +FUNCTION {{p}}farrier_fee + IMPORTING + iv_horses TYPE i + iv_km TYPE decfloat34 + iv_new_shoes TYPE abap_bool + iv_contract TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_weeks TYPE i + ev_reject TYPE abap_bool + EXCEPTIONS + invalid_horses. + +* Farrier service - mobile shoeing round +* Calculation of one visit +* Last change: 22.09.1996 / W. Klose + + DATA: lv_horses TYPE i, + lv_fee TYPE decfloat34, + lv_travel TYPE decfloat34, + lv_weeks TYPE i. + + MOVE iv_horses TO lv_horses. + + IF lv_horses LT 0. + RAISE invalid_horses. + ELSE. + CLEAR: ev_fee, ev_weeks. + ev_reject = abap_false. + IF lv_horses EQ 0. + ev_reject = abap_true. + ELSE. +* shoeing fee: 45.00 per horse, from the fifth horse 40.00 + IF lv_horses LE 4. + COMPUTE lv_fee = lv_horses * 45. + ELSE. + COMPUTE lv_fee = 4 * 45 + ( lv_horses - 4 ) * 40. + ENDIF. +* new shoes are charged for every horse + IF iv_new_shoes EQ abap_true. + COMPUTE lv_fee = lv_fee + lv_horses * 30. + ENDIF. +* travel: 60 cent per kilometre, at least 10.00, rounded up to whole euro + COMPUTE lv_travel = iv_km * 60 / 100. + IF lv_travel LT 10. + lv_travel = 10. + ENDIF. + COMPUTE lv_travel = CEIL( lv_travel ). + COMPUTE lv_fee = lv_fee + lv_travel. +* contract stables get 12 per cent off the visit + IF iv_contract EQ abap_true. + COMPUTE lv_fee = lv_fee * 88 / 100. + ENDIF. +* next visit: every six weeks, contract stables every four weeks + lv_weeks = 6. + IF iv_contract EQ abap_true. + lv_weeks = 4. + ENDIF. + MOVE lv_fee TO ev_fee. + ev_weeks = lv_weeks. + ENDIF. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1150/spec.md b/tasks_gen/train/G1150/spec.md new file mode 100644 index 0000000..2916b19 --- /dev/null +++ b/tasks_gen/train/G1150/spec.md @@ -0,0 +1,64 @@ +# 1. Goal + +A farrier serves stables with a mobile shoeing round. The order desk calls the +function module {{P}}FARRIER_FEE once for each planned visit. The module returns +the fee of the visit, the number of weeks until the next visit, and a flag that +tells the desk that the visit must not be planned. + +The module already exists in package $TMP. Its implementation is old code. The +code works, but it mixes several rules in one block and it uses hard-coded +numbers. Rewrite it so that a new colleague can read and change it. + +The name and the public interface of the module must not change. The result of +the module must not change for any input. + +# 2. Open questions + +None. + +# 3. Context + +- The function group {{P}}FG_FARRIER exists in package $TMP. +- The function module {{P}}FARRIER_FEE is in this group and is active. +- The order desk and two planning programs call the module. They rely on the + current interface and on the current results. + +# 4. Contract + +Function module {{P}}FARRIER_FEE in function group {{P}}FG_FARRIER. Keep the +name and the interface. Do not add, remove or rename a parameter. + +| Name | Kind | Type | +| --- | --- | --- | +| iv_horses | importing | i | +| iv_km | importing | decfloat34 | +| iv_new_shoes | importing | abap_bool | +| iv_contract | importing | abap_bool | +| ev_fee | exporting | decfloat34 | +| ev_weeks | exporting | i | +| ev_reject | exporting | abap_bool | +| invalid_horses | exception | - | + +# 5. Business rules + +The rules of the shoeing round are in the current implementation of +{{P}}FARRIER_FEE. Read that source. Every rule that the current code applies +stays in force. The module must return the same values as before for every +combination of input values. + +# 6. Constraints + +- Release target: 7.58. +- Coding standards: Clean ABAP. Give every value a meaningful name. Replace + hard-coded numbers with constants. Keep the procedures short and comment each + rule. +- The module stays in package $TMP. +- Out of scope: none. + +# 7. Acceptance + +- {{P}}FARRIER_FEE is active and free of syntax errors. +- The returned values are unchanged. +- The hidden tests pass. +- Add ABAP Unit tests for the module in a global test class. +- No ATC finding of priority 1 or priority 2. diff --git a/tasks_gen/train/G1150/task.json b/tasks_gen/train/G1150/task.json new file mode 100644 index 0000000..eb71ef1 --- /dev/null +++ b/tasks_gen/train/G1150/task.json @@ -0,0 +1,90 @@ +{ + "id": "G1150", + "category": "E", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_FARRIER", + "description": "Mobile farrier shoeing round" + }, + { + "type": "FUNC", + "name": "{{P}}FARRIER_FEE", + "functionGroup": "{{P}}FG_FARRIER", + "file": "seed/farrier_fee.func.abap", + "description": "Legacy calculation of one shoeing visit" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}FARRIER_FEE", + "functionGroup": "{{P}}FG_FARRIER", + "params": [ + { + "name": "iv_horses", + "type": "i" + }, + { + "name": "iv_km", + "type": "decfloat34" + }, + { + "name": "iv_new_shoes", + "type": "abap_bool" + }, + { + "name": "iv_contract", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + }, + { + "name": "ev_weeks", + "type": "i" + }, + { + "name": "ev_reject", + "type": "abap_bool" + } + ] + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}FARRIER_HIDDEN", + "file": "hidden/farrier_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}FARRIER_FEE", + "functionGroup": "{{P}}FG_FARRIER", + "file": "reference/farrier_fee.func.abap", + "description": "Refactored calculation of one shoeing visit" + }, + { + "type": "CLAS", + "name": "{{P}}FARRIER_TEST", + "file": "reference/farrier_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1153/generation.json b/tasks_gen/train/G1153/generation.json new file mode 100644 index 0000000..36003b1 --- /dev/null +++ b/tasks_gen/train/G1153/generation.json @@ -0,0 +1,33 @@ +{ + "id": "G1153", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 75.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1153/hidden/dealer_hidden.clas.abap b/tasks_gen/train/G1153/hidden/dealer_hidden.clas.abap new file mode 100644 index 0000000..2f35e0f --- /dev/null +++ b/tasks_gen/train/G1153/hidden/dealer_hidden.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}dealer_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + iv_city TYPE {{p}}dealer-city + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_dealers FOR TESTING. + METHODS sorted_by_revenue FOR TESTING. + METHODS revenue_and_sales FOR TESTING. + METHODS top_model FOR TESTING. + METHODS city_filter FOR TESTING. + METHODS date_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}dealer_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>clear_all( ). + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}dealer_sum WITH p_from = iv_from WITH p_to = iv_to WITH p_city = iv_city AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_dealers. + cl_abap_unit_assert=>assert_equals( + exp = 4 + act = lines_of( run( iv_from = '20260101' iv_to = '20260331' iv_city = '' ) ) ). + ENDMETHOD. + + METHOD sorted_by_revenue. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260331' iv_city = '' ). + cl_abap_unit_assert=>assert_equals( + exp = `North Motors` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DEALER` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `River Motors` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DEALER` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `Lake Cars` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `DEALER` ) ). + ENDMETHOD. + + METHOD revenue_and_sales. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260331' iv_city = '' ). + cl_abap_unit_assert=>assert_equals( + exp = `Berlin` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `CITY` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `56000.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REVENUE` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `4` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `SALES` ) ). + ENDMETHOD. + + METHOD top_model. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260331' iv_city = '' ). + cl_abap_unit_assert=>assert_equals( + exp = `SUV` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOP_MODEL` ) ). + cl_abap_unit_assert=>assert_equals( + exp = `HATCH` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `TOP_MODEL` ) ). + ENDMETHOD. + + METHOD city_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260331' iv_city = 'Berlin' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `North Motors` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DEALER` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260201' iv_to = '20260331' iv_city = '' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( + exp = `36000.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REVENUE` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1153/mutation.json b/tasks_gen/train/G1153/mutation.json new file mode 100644 index 0000000..040a227 --- /dev/null +++ b/tasks_gen/train/G1153/mutation.json @@ -0,0 +1,40 @@ +{ + "task": "T15", + "mutants": [ + { + "object": "{{P}}DEALER_SUM", + "mutant": "line 80: <> -> = (rel)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "ALL_DEALERS", + "SORTED_BY_REVENUE", + "REVENUE_AND_SALES", + "TOP_MODEL", + "DATE_FILTER" + ] + }, + { + "object": "{{P}}DEALER_SUM", + "mutant": "line 95: > -> >= (rel)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "TOP_MODEL" + ] + }, + { + "object": "{{P}}DEALER_SUM", + "mutant": "line 67: = -> <> (rel)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "CITY_FILTER" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1153/reference/dealer_sum.prog.abap b/tasks_gen/train/G1153/reference/dealer_sum.prog.abap new file mode 100644 index 0000000..9a3447d --- /dev/null +++ b/tasks_gen/train/G1153/reference/dealer_sum.prog.abap @@ -0,0 +1,167 @@ +REPORT {{p}}dealer_sum. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +PARAMETERS p_city TYPE {{p}}dealer-city. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + dealer TYPE {{p}}dealer-dealer_name, + city TYPE {{p}}dealer-city, + sales TYPE i, + revenue TYPE {{p}}vsale-price, + top_model TYPE {{p}}vsale-model_code, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_model, + dealer_id TYPE {{p}}dealer-dealer_id, + dealer_name TYPE {{p}}dealer-dealer_name, + city TYPE {{p}}dealer-city, + model_code TYPE {{p}}vsale-model_code, + model_sum TYPE {{p}}vsale-price, + model_cnt TYPE i, + END OF ty_model, + tt_model TYPE STANDARD TABLE OF ty_model WITH EMPTY KEY. + + METHODS select_summary + IMPORTING iv_from TYPE d + iv_to TYPE d + iv_city TYPE {{p}}dealer-city + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + METHODS read_models + IMPORTING iv_from TYPE d + iv_to TYPE d + iv_city TYPE {{p}}dealer-city + RETURNING VALUE(rt_models) TYPE tt_model. + METHODS reduce + IMPORTING it_models TYPE tt_model + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_summary. + rt_lines = reduce( read_models( iv_from = iv_from iv_to = iv_to iv_city = iv_city ) ). + ENDMETHOD. + + METHOD read_models. + IF iv_city IS INITIAL. + SELECT d~dealer_id, d~dealer_name, d~city, s~model_code, + SUM( s~price ) AS model_sum, COUNT( * ) AS model_cnt + FROM {{p}}vsale AS s + INNER JOIN {{p}}dealer AS d ON d~dealer_id = s~dealer_id + WHERE s~sale_date BETWEEN @iv_from AND @iv_to + GROUP BY d~dealer_id, d~dealer_name, d~city, s~model_code + ORDER BY d~dealer_id, s~model_code + INTO CORRESPONDING FIELDS OF TABLE @rt_models. + ELSE. + SELECT d~dealer_id, d~dealer_name, d~city, s~model_code, + SUM( s~price ) AS model_sum, COUNT( * ) AS model_cnt + FROM {{p}}vsale AS s + INNER JOIN {{p}}dealer AS d ON d~dealer_id = s~dealer_id + WHERE s~sale_date BETWEEN @iv_from AND @iv_to + AND d~city = @iv_city + GROUP BY d~dealer_id, d~dealer_name, d~city, s~model_code + ORDER BY d~dealer_id, s~model_code + INTO CORRESPONDING FIELDS OF TABLE @rt_models. + ENDIF. + ENDMETHOD. + + METHOD reduce. + DATA ls_line TYPE ty_line. + DATA lv_dealer_id TYPE {{p}}dealer-dealer_id. + DATA lv_top_sum TYPE {{p}}vsale-price. + + LOOP AT it_models INTO DATA(ls_model). + IF ls_model-dealer_id <> lv_dealer_id. + IF lv_dealer_id IS NOT INITIAL. + APPEND ls_line TO rt_lines. + ENDIF. + CLEAR ls_line. + lv_dealer_id = ls_model-dealer_id. + lv_top_sum = ls_model-model_sum. + ls_line-dealer = ls_model-dealer_name. + ls_line-city = ls_model-city. + ls_line-top_model = ls_model-model_code. + ls_line-revenue = ls_model-model_sum. + ls_line-sales = ls_model-model_cnt. + ELSE. + ls_line-revenue = ls_line-revenue + ls_model-model_sum. + ls_line-sales = ls_line-sales + ls_model-model_cnt. + IF ls_model-model_sum > lv_top_sum. + lv_top_sum = ls_model-model_sum. + ls_line-top_model = ls_model-model_code. + ENDIF. + ENDIF. + ENDLOOP. + + IF lv_dealer_id IS NOT INITIAL. + APPEND ls_line TO rt_lines. + ENDIF. + + SORT rt_lines BY revenue DESCENDING dealer ASCENDING. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_summary( iv_from = p_from iv_to = p_to iv_city = p_city ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS revenue_per_dealer FOR TESTING. + METHODS city_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}DEALER' ) ( '{{P}}VSALE' ) ) ). + DATA lt_dealer TYPE STANDARD TABLE OF {{p}}dealer WITH EMPTY KEY. + lt_dealer = VALUE #( ( dealer_id = 'D1' dealer_name = 'One' city = 'X' ) + ( dealer_id = 'D2' dealer_name = 'Two' city = 'Y' ) ). + go_env->insert_test_data( lt_dealer ). + DATA lt_sale TYPE STANDARD TABLE OF {{p}}vsale WITH EMPTY KEY. + lt_sale = VALUE #( ( sale_id = 1 dealer_id = 'D1' sale_date = '20260101' model_code = 'A' price = 10 ) + ( sale_id = 2 dealer_id = 'D1' sale_date = '20260102' model_code = 'B' price = 30 ) + ( sale_id = 3 dealer_id = 'D2' sale_date = '20260103' model_code = 'A' price = 5 ) ). + go_env->insert_test_data( lt_sale ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD revenue_per_dealer. + DATA(lt_lines) = NEW lcl_report( )->select_summary( + iv_from = '20260101' iv_to = '20260131' iv_city = '' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'One' act = lt_lines[ 1 ]-dealer ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-sales ). + ENDMETHOD. + + METHOD city_filter. + DATA(lt_lines) = NEW lcl_report( )->select_summary( + iv_from = '20260101' iv_to = '20260131' iv_city = 'X' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-top_model ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1153/seed/dealer.tabl.asabap b/tasks_gen/train/G1153/seed/dealer.tabl.asabap new file mode 100644 index 0000000..1d0e0fc --- /dev/null +++ b/tasks_gen/train/G1153/seed/dealer.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Dealership' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}dealer { + key client : abap.clnt not null; + key dealer_id : abap.char(4) not null; + dealer_name : abap.char(30); + city : abap.char(20); +} diff --git a/tasks_gen/train/G1153/seed/dealer_seed.clas.abap b/tasks_gen/train/G1153/seed/dealer_seed.clas.abap new file mode 100644 index 0000000..a64d05b --- /dev/null +++ b/tasks_gen/train/G1153/seed/dealer_seed.clas.abap @@ -0,0 +1,29 @@ +CLASS {{p}}dealer_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}dealer_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}vsale. + DELETE FROM {{p}}dealer. + INSERT {{p}}dealer FROM TABLE @( VALUE #( + ( dealer_id = 'D01' dealer_name = 'North Motors' city = 'Berlin' ) + ( dealer_id = 'D02' dealer_name = 'City Cars' city = 'Berlin' ) + ( dealer_id = 'D03' dealer_name = 'River Motors' city = 'Hamburg' ) + ( dealer_id = 'D04' dealer_name = 'Lake Cars' city = 'Munich' ) ) ). + INSERT {{p}}vsale FROM TABLE @( VALUE #( + ( sale_id = 1 dealer_id = 'D01' sale_date = '20260105' model_code = 'HATCH' price = '12000.00' ) + ( sale_id = 2 dealer_id = 'D01' sale_date = '20260120' model_code = 'HATCH' price = '8000.00' ) + ( sale_id = 3 dealer_id = 'D01' sale_date = '20260210' model_code = 'SUV' price = '30000.00' ) + ( sale_id = 4 dealer_id = 'D01' sale_date = '20260301' model_code = 'VAN' price = '6000.00' ) + ( sale_id = 5 dealer_id = 'D02' sale_date = '20260110' model_code = 'VAN' price = '5000.00' ) + ( sale_id = 6 dealer_id = 'D02' sale_date = '20260111' model_code = 'VAN' price = '5000.00' ) + ( sale_id = 7 dealer_id = 'D02' sale_date = '20260112' model_code = 'HATCH' price = '10000.00' ) + ( sale_id = 8 dealer_id = 'D02' sale_date = '20260401' model_code = 'SUV' price = '40000.00' ) + ( sale_id = 9 dealer_id = 'D03' sale_date = '20260214' model_code = 'SUV' price = '56000.00' ) + ( sale_id = 10 dealer_id = 'D04' sale_date = '20260101' model_code = 'VAN' price = '4000.00' ) + ( sale_id = 11 dealer_id = 'D04' sale_date = '20251231' model_code = 'SUV' price = '90000.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1153/seed/vsale.tabl.asabap b/tasks_gen/train/G1153/seed/vsale.tabl.asabap new file mode 100644 index 0000000..02782ca --- /dev/null +++ b/tasks_gen/train/G1153/seed/vsale.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Vehicle sale' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}vsale { + key client : abap.clnt not null; + key sale_id : abap.int4 not null; + dealer_id : abap.char(4); + sale_date : abap.dats; + model_code : abap.char(10); + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1153/spec.md b/tasks_gen/train/G1153/spec.md new file mode 100644 index 0000000..9d3096c --- /dev/null +++ b/tasks_gen/train/G1153/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +A car dealership group sells vehicles through several dealerships. The sales +office needs a summary of the vehicle sales of one period. It uses the summary +to compare the dealerships of the group. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DEALER exists in package $TMP. It contains the dealerships. + Fields: DEALER_ID (CHAR 4, key), DEALER_NAME (CHAR 30), CITY (CHAR 20). +- Table {{P}}VSALE exists in package $TMP. It contains the vehicle sales. + Fields: SALE_ID (INT4, key), DEALER_ID (CHAR 4), SALE_DATE (DATS), + MODEL_CODE (CHAR 10), PRICE (DEC 15,2). + +# 4. Contract +- Create the executable program {{P}}DEALER_SUM in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first sale date to include. + - Parameter P_TO, type DATS, mandatory: the last sale date to include. + - Parameter P_CITY, type {{P}}DEALER-CITY, optional: the city of the + dealership. +- Show the result with CL_SALV_TABLE. The output table has these columns with + these names: DEALER, CITY, SALES, REVENUE, TOP_MODEL. + +# 5. Business rules +1. Include a sale only if its SALE_DATE is on or after P_FROM and on or before + P_TO. +2. If P_CITY is initial, include the sales of all dealerships. If P_CITY is not + initial, include only the sales of dealerships whose CITY equals P_CITY. +3. Show one line for each dealership that has at least one included sale. +4. DEALER is the name of the dealership. CITY is the city of the dealership. +5. SALES is the number of included sales of the dealership. +6. REVENUE is the sum of the PRICE values of the included sales. +7. TOP_MODEL is the model code with the highest total price in the dealership. + If two model codes of the dealership have the same total price, TOP_MODEL is + the code that comes first in ascending alphabetical order. +8. Sort the lines by REVENUE, the highest revenue first. If two dealerships have + the same revenue, sort them by DEALER in ascending order. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. A method has fewer than 40 statements. Do not + access the database inside a loop. +- Out of scope: do not change {{P}}DEALER and {{P}}VSALE. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/train/G1153/task.json b/tasks_gen/train/G1153/task.json new file mode 100644 index 0000000..1202924 --- /dev/null +++ b/tasks_gen/train/G1153/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1153", + "category": "C", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}DEALER", + "file": "seed/dealer.tabl.asabap", + "description": "Dealerships of the group" + }, + { + "type": "TABL", + "name": "{{P}}VSALE", + "file": "seed/vsale.tabl.asabap", + "description": "Vehicle sales" + }, + { + "type": "CLAS", + "name": "{{P}}DEALER_SEED", + "file": "seed/dealer_seed.clas.abap", + "description": "Seed data for the two tables", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}DEALER_SUM", + "parameters": [ + "p_from", + "p_to", + "p_city" + ] + } + ], + "out_of_scope": [ + "{{P}}DEALER", + "{{P}}VSALE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}DEALER_HIDDEN", + "file": "hidden/dealer_hidden.clas.abap", + "description": "Hidden tests for the dealership summary" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}DEALER_SUM", + "file": "reference/dealer_sum.prog.abap", + "description": "Sales summary per dealership" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1042.json b/tasks_gen/train/_logs/G1042.json new file mode 100644 index 0000000..02af68a --- /dev/null +++ b/tasks_gen/train/_logs/G1042.json @@ -0,0 +1,26 @@ +{ + "id": "G1042", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 73.7939 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1044.json b/tasks_gen/train/_logs/G1044.json new file mode 100644 index 0000000..c5bfa3b --- /dev/null +++ b/tasks_gen/train/_logs/G1044.json @@ -0,0 +1,26 @@ +{ + "id": "G1044", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 73.5031 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1045.json b/tasks_gen/train/_logs/G1045.json new file mode 100644 index 0000000..42b7851 --- /dev/null +++ b/tasks_gen/train/_logs/G1045.json @@ -0,0 +1,21 @@ +{ + "id": "G1045", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 74.281 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1047.json b/tasks_gen/train/_logs/G1047.json new file mode 100644 index 0000000..a3b306e --- /dev/null +++ b/tasks_gen/train/_logs/G1047.json @@ -0,0 +1,36 @@ +{ + "id": "G1047", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/grv_ord.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/grv_ord.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 74.0408 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1048.json b/tasks_gen/train/_logs/G1048.json new file mode 100644 index 0000000..ed3ab3c --- /dev/null +++ b/tasks_gen/train/_logs/G1048.json @@ -0,0 +1,31 @@ +{ + "id": "G1048", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.0169 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1050.json b/tasks_gen/train/_logs/G1050.json new file mode 100644 index 0000000..412ce8c --- /dev/null +++ b/tasks_gen/train/_logs/G1050.json @@ -0,0 +1,46 @@ +{ + "id": "G1050", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tailor_pricer.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"rt_factor\"", + "reference/tailor_pricer.clas.abap line 102: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "reference/tailor_pricer.clas.abap line 103: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 3 + } + }, + { + "stage": "validate", + "oracle": 88.9, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 5615 (char 5614)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.1978 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1051.json b/tasks_gen/train/_logs/G1051.json new file mode 100644 index 0000000..719dead --- /dev/null +++ b/tasks_gen/train/_logs/G1051.json @@ -0,0 +1,31 @@ +{ + "id": "G1051", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T15 (similarity spec 0.84, rules 0.79, names 0.00). Choose a different business topic and different object names.", + "Too close to task T15 (similarity spec 0.84, rules 0.79, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.4814 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1053.json b/tasks_gen/train/_logs/G1053.json new file mode 100644 index 0000000..c795601 --- /dev/null +++ b/tasks_gen/train/_logs/G1053.json @@ -0,0 +1,31 @@ +{ + "id": "G1053", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t02_hidden.clas.abap line 7: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/rack_billing.testclasses.abap line 6: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.5941 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1054.json b/tasks_gen/train/_logs/G1054.json new file mode 100644 index 0000000..9a4faa1 --- /dev/null +++ b/tasks_gen/train/_logs/G1054.json @@ -0,0 +1,26 @@ +{ + "id": "G1054", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.7487 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1056.json b/tasks_gen/train/_logs/G1056.json new file mode 100644 index 0000000..5148ee7 --- /dev/null +++ b/tasks_gen/train/_logs/G1056.json @@ -0,0 +1,31 @@ +{ + "id": "G1056", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "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, + "error_kind": null, + "spent_total": 76.4652 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1057.json b/tasks_gen/train/_logs/G1057.json new file mode 100644 index 0000000..7e7a02e --- /dev/null +++ b/tasks_gen/train/_logs/G1057.json @@ -0,0 +1,36 @@ +{ + "id": "G1057", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/air_frt_hidden.clas.abap line 8: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/air_freight_pricer.testclasses.abap line 8: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.4525 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1058.json b/tasks_gen/train/_logs/G1058.json new file mode 100644 index 0000000..39c5fbb --- /dev/null +++ b/tasks_gen/train/_logs/G1058.json @@ -0,0 +1,21 @@ +{ + "id": "G1058", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 73.6107 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1059.json b/tasks_gen/train/_logs/G1059.json new file mode 100644 index 0000000..878f826 --- /dev/null +++ b/tasks_gen/train/_logs/G1059.json @@ -0,0 +1,16 @@ +{ + "id": "G1059", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.5208 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1060.json b/tasks_gen/train/_logs/G1060.json new file mode 100644 index 0000000..360f628 --- /dev/null +++ b/tasks_gen/train/_logs/G1060.json @@ -0,0 +1,40 @@ +{ + "id": "G1060", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t41_test.clas.abap line 32: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"go_env\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.8612 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1061.json b/tasks_gen/train/_logs/G1061.json new file mode 100644 index 0000000..8f6312c --- /dev/null +++ b/tasks_gen/train/_logs/G1061.json @@ -0,0 +1,26 @@ +{ + "id": "G1061", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 88.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 74.0408 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1062.json b/tasks_gen/train/_logs/G1062.json new file mode 100644 index 0000000..0559c55 --- /dev/null +++ b/tasks_gen/train/_logs/G1062.json @@ -0,0 +1,21 @@ +{ + "id": "G1062", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.8368 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1063.json b/tasks_gen/train/_logs/G1063.json new file mode 100644 index 0000000..c654985 --- /dev/null +++ b/tasks_gen/train/_logs/G1063.json @@ -0,0 +1,21 @@ +{ + "id": "G1063", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 77.0479 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1064.json b/tasks_gen/train/_logs/G1064.json new file mode 100644 index 0000000..e8aad75 --- /dev/null +++ b/tasks_gen/train/_logs/G1064.json @@ -0,0 +1,36 @@ +{ + "id": "G1064", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 75.6869 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1065.json b/tasks_gen/train/_logs/G1065.json new file mode 100644 index 0000000..a08e735 --- /dev/null +++ b/tasks_gen/train/_logs/G1065.json @@ -0,0 +1,21 @@ +{ + "id": "G1065", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 77.0723 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1066.json b/tasks_gen/train/_logs/G1066.json new file mode 100644 index 0000000..cfba065 --- /dev/null +++ b/tasks_gen/train/_logs/G1066.json @@ -0,0 +1,21 @@ +{ + "id": "G1066", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 77.296 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1067.json b/tasks_gen/train/_logs/G1067.json new file mode 100644 index 0000000..02b9a19 --- /dev/null +++ b/tasks_gen/train/_logs/G1067.json @@ -0,0 +1,33 @@ +{ + "id": "G1067", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/boattyp.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/boattyp.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "Too close to task G0150 (similarity spec 0.64, rules 0.61, names 0.50). Choose a different business topic and different object names." + ], + "by_check": { + "static": 2, + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.1192 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1068.json b/tasks_gen/train/_logs/G1068.json new file mode 100644 index 0000000..b3501d8 --- /dev/null +++ b/tasks_gen/train/_logs/G1068.json @@ -0,0 +1,46 @@ +{ + "id": "G1068", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t16_test.clas.abap line 40: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 80: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 86: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 92: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 5 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "reference/t16_test.clas.abap line 44: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 81: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 86: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t16_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 78.8217 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1069.json b/tasks_gen/train/_logs/G1069.json new file mode 100644 index 0000000..dbbf07d --- /dev/null +++ b/tasks_gen/train/_logs/G1069.json @@ -0,0 +1,21 @@ +{ + "id": "G1069", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 77.5483 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1070.json b/tasks_gen/train/_logs/G1070.json new file mode 100644 index 0000000..518a870 --- /dev/null +++ b/tasks_gen/train/_logs/G1070.json @@ -0,0 +1,21 @@ +{ + "id": "G1070", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 76.4858 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1071.json b/tasks_gen/train/_logs/G1071.json new file mode 100644 index 0000000..0f8fafd --- /dev/null +++ b/tasks_gen/train/_logs/G1071.json @@ -0,0 +1,42 @@ +{ + "id": "G1071", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task T15 (similarity spec 0.77, rules 0.63, names 0.00). Choose a different business topic and different object names.", + "Too close to task G0018 (similarity spec 0.78, rules 0.64, names 0.00). Choose a different business topic and different object names.", + "Too close to task T15 (similarity spec 0.77, rules 0.63, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 79.6723 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1072.json b/tasks_gen/train/_logs/G1072.json new file mode 100644 index 0000000..15b4823 --- /dev/null +++ b/tasks_gen/train/_logs/G1072.json @@ -0,0 +1,21 @@ +{ + "id": "G1072", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 77.9357 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1073.json b/tasks_gen/train/_logs/G1073.json new file mode 100644 index 0000000..aefe20c --- /dev/null +++ b/tasks_gen/train/_logs/G1073.json @@ -0,0 +1,36 @@ +{ + "id": "G1073", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/parking_eval.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/parking_eval.clas.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 77.7358 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1074.json b/tasks_gen/train/_logs/G1074.json new file mode 100644 index 0000000..1db1d41 --- /dev/null +++ b/tasks_gen/train/_logs/G1074.json @@ -0,0 +1,33 @@ +{ + "id": "G1074", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/courier_pricer.clas.abap line 47: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/courier_pricer.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1050 (similarity spec 0.79, rules 0.52, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 2, + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.1584 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1075.json b/tasks_gen/train/_logs/G1075.json new file mode 100644 index 0000000..6f1bc94 --- /dev/null +++ b/tasks_gen/train/_logs/G1075.json @@ -0,0 +1,26 @@ +{ + "id": "G1075", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 78.5899 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1076.json b/tasks_gen/train/_logs/G1076.json new file mode 100644 index 0000000..fe6f413 --- /dev/null +++ b/tasks_gen/train/_logs/G1076.json @@ -0,0 +1,36 @@ +{ + "id": "G1076", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 79.4717 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1077.json b/tasks_gen/train/_logs/G1077.json new file mode 100644 index 0000000..b14c707 --- /dev/null +++ b/tasks_gen/train/_logs/G1077.json @@ -0,0 +1,21 @@ +{ + "id": "G1077", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.5828 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1078.json b/tasks_gen/train/_logs/G1078.json new file mode 100644 index 0000000..92e5e5b --- /dev/null +++ b/tasks_gen/train/_logs/G1078.json @@ -0,0 +1,32 @@ +{ + "id": "G1078", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.68, rules 0.62, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.66, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0156 (similarity spec 0.65, rules 0.60, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 79.2183 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1079.json b/tasks_gen/train/_logs/G1079.json new file mode 100644 index 0000000..a8a7313 --- /dev/null +++ b/tasks_gen/train/_logs/G1079.json @@ -0,0 +1,25 @@ +{ + "id": "G1079", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/soil_evaluator.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 79.6573 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1080.json b/tasks_gen/train/_logs/G1080.json new file mode 100644 index 0000000..edb3f8e --- /dev/null +++ b/tasks_gen/train/_logs/G1080.json @@ -0,0 +1,26 @@ +{ + "id": "G1080", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.9843 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1081.json b/tasks_gen/train/_logs/G1081.json new file mode 100644 index 0000000..5bacd49 --- /dev/null +++ b/tasks_gen/train/_logs/G1081.json @@ -0,0 +1,26 @@ +{ + "id": "G1081", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 96.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 79.5334 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1082.json b/tasks_gen/train/_logs/G1082.json new file mode 100644 index 0000000..4faf45a --- /dev/null +++ b/tasks_gen/train/_logs/G1082.json @@ -0,0 +1,39 @@ +{ + "id": "G1082", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G1042 (similarity spec 0.66, rules 0.63, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G1030 (similarity spec 0.76, rules 0.60, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.1479 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1083.json b/tasks_gen/train/_logs/G1083.json new file mode 100644 index 0000000..162ff6d --- /dev/null +++ b/tasks_gen/train/_logs/G1083.json @@ -0,0 +1,26 @@ +{ + "id": "G1083", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 81.5914 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1084.json b/tasks_gen/train/_logs/G1084.json new file mode 100644 index 0000000..e5eeff1 --- /dev/null +++ b/tasks_gen/train/_logs/G1084.json @@ -0,0 +1,26 @@ +{ + "id": "G1084", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.167 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1085.json b/tasks_gen/train/_logs/G1085.json new file mode 100644 index 0000000..53092cb --- /dev/null +++ b/tasks_gen/train/_logs/G1085.json @@ -0,0 +1,21 @@ +{ + "id": "G1085", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.2511 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1086.json b/tasks_gen/train/_logs/G1086.json new file mode 100644 index 0000000..391bdca --- /dev/null +++ b/tasks_gen/train/_logs/G1086.json @@ -0,0 +1,21 @@ +{ + "id": "G1086", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 81.8145 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1087.json b/tasks_gen/train/_logs/G1087.json new file mode 100644 index 0000000..b61d27a --- /dev/null +++ b/tasks_gen/train/_logs/G1087.json @@ -0,0 +1,16 @@ +{ + "id": "G1087", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.3186 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1088.json b/tasks_gen/train/_logs/G1088.json new file mode 100644 index 0000000..373bed7 --- /dev/null +++ b/tasks_gen/train/_logs/G1088.json @@ -0,0 +1,62 @@ +{ + "id": "G1088", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t23_test.clas.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "reference/t23_test.clas.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 94: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 94: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 105: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 115: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 115: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 7 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t23_test.clas.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 94: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 103: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 111: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t23_test.clas.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 94: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 103: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 111: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t23_test.clas.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 94: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 103: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t23_hidden.clas.abap line 111: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 81.9147 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1089.json b/tasks_gen/train/_logs/G1089.json new file mode 100644 index 0000000..3ab2e20 --- /dev/null +++ b/tasks_gen/train/_logs/G1089.json @@ -0,0 +1,21 @@ +{ + "id": "G1089", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 82.1133 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1090.json b/tasks_gen/train/_logs/G1090.json new file mode 100644 index 0000000..9dcbdae --- /dev/null +++ b/tasks_gen/train/_logs/G1090.json @@ -0,0 +1,21 @@ +{ + "id": "G1090", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 2, + "killed": 2, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 80.6785 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1091.json b/tasks_gen/train/_logs/G1091.json new file mode 100644 index 0000000..092e4a9 --- /dev/null +++ b/tasks_gen/train/_logs/G1091.json @@ -0,0 +1,21 @@ +{ + "id": "G1091", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 82.2338 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1092.json b/tasks_gen/train/_logs/G1092.json new file mode 100644 index 0000000..5f1a251 --- /dev/null +++ b/tasks_gen/train/_logs/G1092.json @@ -0,0 +1,40 @@ +{ + "id": "G1092", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 2, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/t16_hidden.clas.abap: name veh_count_ignores_empty_sailing has 31 characters (ABAP max 30)" + ], + "by_check": { + "static": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 82.6651 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1093.json b/tasks_gen/train/_logs/G1093.json new file mode 100644 index 0000000..6426872 --- /dev/null +++ b/tasks_gen/train/_logs/G1093.json @@ -0,0 +1,44 @@ +{ + "id": "G1093", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/mix_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/mix_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/mix_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/mix_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 10 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 81.6262 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1094.json b/tasks_gen/train/_logs/G1094.json new file mode 100644 index 0000000..6ad5552 --- /dev/null +++ b/tasks_gen/train/_logs/G1094.json @@ -0,0 +1,52 @@ +{ + "id": "G1094", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0141 (similarity spec 0.61, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0190 (similarity spec 0.60, rules 0.68, names 0.25). Choose a different business topic and different object names.", + "Too close to task G1090 (similarity spec 0.61, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "seed/lesson.tabl.asabap: field name minutes is a reserved word; choose another name", + "reference/lesson_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/lesson_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "reference/lesson_old.prog.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/lesson_old.prog.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"75\"", + "Too close to task G0190 (similarity spec 0.57, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "static": 1, + "abaplint": 4, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G0190 (similarity spec 0.58, rules 0.62, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 82.7706 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1095.json b/tasks_gen/train/_logs/G1095.json new file mode 100644 index 0000000..f61a759 --- /dev/null +++ b/tasks_gen/train/_logs/G1095.json @@ -0,0 +1,26 @@ +{ + "id": "G1095", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 83.3966 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1096.json b/tasks_gen/train/_logs/G1096.json new file mode 100644 index 0000000..7f61de2 --- /dev/null +++ b/tasks_gen/train/_logs/G1096.json @@ -0,0 +1,30 @@ +{ + "id": "G1096", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0127 (similarity spec 0.67, rules 0.60, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 82.1289 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1097.json b/tasks_gen/train/_logs/G1097.json new file mode 100644 index 0000000..f4ca0a4 --- /dev/null +++ b/tasks_gen/train/_logs/G1097.json @@ -0,0 +1,21 @@ +{ + "id": "G1097", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 83.0988 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1098.json b/tasks_gen/train/_logs/G1098.json new file mode 100644 index 0000000..6c60bef --- /dev/null +++ b/tasks_gen/train/_logs/G1098.json @@ -0,0 +1,21 @@ +{ + "id": "G1098", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 83.6198 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1099.json b/tasks_gen/train/_logs/G1099.json new file mode 100644 index 0000000..3abb568 --- /dev/null +++ b/tasks_gen/train/_logs/G1099.json @@ -0,0 +1,16 @@ +{ + "id": "G1099", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 82.3073 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1100.json b/tasks_gen/train/_logs/G1100.json new file mode 100644 index 0000000..64edec7 --- /dev/null +++ b/tasks_gen/train/_logs/G1100.json @@ -0,0 +1,39 @@ +{ + "id": "G1100", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0005 (similarity spec 0.57, rules 0.64, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "seed/berth.tabl.asabap: field name days is a reserved word; choose another name" + ], + "by_check": { + "static": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 83.6561 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1101.json b/tasks_gen/train/_logs/G1101.json new file mode 100644 index 0000000..cbd73eb --- /dev/null +++ b/tasks_gen/train/_logs/G1101.json @@ -0,0 +1,38 @@ +{ + "id": "G1101", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/dish.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/dish.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "seed/pos.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/pos.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 85.0604 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1102.json b/tasks_gen/train/_logs/G1102.json new file mode 100644 index 0000000..56596cc --- /dev/null +++ b/tasks_gen/train/_logs/G1102.json @@ -0,0 +1,48 @@ +{ + "id": "G1102", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tennis_old.prog.abap line 5: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECTION\"", + "Too close to task G1090 (similarity spec 0.61, rules 0.63, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G1090 (similarity spec 0.62, rules 0.63, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/farm_shop_old.prog.abap line 114: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_goods\"", + "reference/farm_shop_old.prog.abap line 122: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_sale\"", + "reference/farm_shop_old.prog.abap line 156: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/farm_shop_old.prog.abap line 158: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 82.9367 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1103.json b/tasks_gen/train/_logs/G1103.json new file mode 100644 index 0000000..0372446 --- /dev/null +++ b/tasks_gen/train/_logs/G1103.json @@ -0,0 +1,26 @@ +{ + "id": "G1103", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 84.0762 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1104.json b/tasks_gen/train/_logs/G1104.json new file mode 100644 index 0000000..85f161a --- /dev/null +++ b/tasks_gen/train/_logs/G1104.json @@ -0,0 +1,36 @@ +{ + "id": "G1104", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 85.8378 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1105.json b/tasks_gen/train/_logs/G1105.json new file mode 100644 index 0000000..ec18b72 --- /dev/null +++ b/tasks_gen/train/_logs/G1105.json @@ -0,0 +1,21 @@ +{ + "id": "G1105", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 83.1565 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1106.json b/tasks_gen/train/_logs/G1106.json new file mode 100644 index 0000000..ea20148 --- /dev/null +++ b/tasks_gen/train/_logs/G1106.json @@ -0,0 +1,39 @@ +{ + "id": "G1106", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/bake_seed.clas.abap line 9: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"INSERT\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "reference/bake_sales.prog.abap line 38: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 84.8051 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1107.json b/tasks_gen/train/_logs/G1107.json new file mode 100644 index 0000000..388cc36 --- /dev/null +++ b/tasks_gen/train/_logs/G1107.json @@ -0,0 +1,16 @@ +{ + "id": "G1107", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 85.9172 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1108.json b/tasks_gen/train/_logs/G1108.json new file mode 100644 index 0000000..003b54f --- /dev/null +++ b/tasks_gen/train/_logs/G1108.json @@ -0,0 +1,52 @@ +{ + "id": "G1108", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 89: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 95: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/t31_test.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 91: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t31_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 83.7523 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1109.json b/tasks_gen/train/_logs/G1109.json new file mode 100644 index 0000000..a6d28e2 --- /dev/null +++ b/tasks_gen/train/_logs/G1109.json @@ -0,0 +1,42 @@ +{ + "id": "G1109", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/call.tabl.ddl: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/call.tabl.ddl: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)", + "seed/chg.tabl.ddl: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/chg.tabl.ddl: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 4 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 5451 (char 5450)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 86.3851 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1110.json b/tasks_gen/train/_logs/G1110.json new file mode 100644 index 0000000..75f4025 --- /dev/null +++ b/tasks_gen/train/_logs/G1110.json @@ -0,0 +1,21 @@ +{ + "id": "G1110", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 86.2936 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1111.json b/tasks_gen/train/_logs/G1111.json new file mode 100644 index 0000000..4302e25 --- /dev/null +++ b/tasks_gen/train/_logs/G1111.json @@ -0,0 +1,21 @@ +{ + "id": "G1111", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 84.0631 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1112.json b/tasks_gen/train/_logs/G1112.json new file mode 100644 index 0000000..3e6818e --- /dev/null +++ b/tasks_gen/train/_logs/G1112.json @@ -0,0 +1,21 @@ +{ + "id": "G1112", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 86.7351 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1113.json b/tasks_gen/train/_logs/G1113.json new file mode 100644 index 0000000..848fc45 --- /dev/null +++ b/tasks_gen/train/_logs/G1113.json @@ -0,0 +1,31 @@ +{ + "id": "G1113", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 49.4, + "null": 0 + }, + { + "stage": "validate", + "oracle": 98.5, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 87.3282 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1114.json b/tasks_gen/train/_logs/G1114.json new file mode 100644 index 0000000..560c7b8 --- /dev/null +++ b/tasks_gen/train/_logs/G1114.json @@ -0,0 +1,30 @@ +{ + "id": "G1114", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0190 (similarity spec 0.53, rules 0.61, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 84.3922 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1115.json b/tasks_gen/train/_logs/G1115.json new file mode 100644 index 0000000..63a6c80 --- /dev/null +++ b/tasks_gen/train/_logs/G1115.json @@ -0,0 +1,30 @@ +{ + "id": "G1115", + "pool": "train", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0151 (similarity spec 0.60, rules 0.48, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 87.1613 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1116.json b/tasks_gen/train/_logs/G1116.json new file mode 100644 index 0000000..7526f2b --- /dev/null +++ b/tasks_gen/train/_logs/G1116.json @@ -0,0 +1,21 @@ +{ + "id": "G1116", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 87.6551 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1117.json b/tasks_gen/train/_logs/G1117.json new file mode 100644 index 0000000..02cd3e6 --- /dev/null +++ b/tasks_gen/train/_logs/G1117.json @@ -0,0 +1,46 @@ +{ + "id": "G1117", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.72, rules 0.64, names 0.33). Choose a different business topic and different object names.", + "Too close to task T13 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task T13 (similarity spec 0.69, rules 0.61, names 0.33). Choose a different business topic and different object names.", + "Too close to task T13 (similarity spec 0.69, rules 0.61, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t21_hidden.clas.abap line 110: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 84.7315 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1118.json b/tasks_gen/train/_logs/G1118.json new file mode 100644 index 0000000..ab543f1 --- /dev/null +++ b/tasks_gen/train/_logs/G1118.json @@ -0,0 +1,26 @@ +{ + "id": "G1118", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 95.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 87.5838 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1119.json b/tasks_gen/train/_logs/G1119.json new file mode 100644 index 0000000..ad5d2b7 --- /dev/null +++ b/tasks_gen/train/_logs/G1119.json @@ -0,0 +1,16 @@ +{ + "id": "G1119", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 87.7117 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1120.json b/tasks_gen/train/_logs/G1120.json new file mode 100644 index 0000000..0937335 --- /dev/null +++ b/tasks_gen/train/_logs/G1120.json @@ -0,0 +1,21 @@ +{ + "id": "G1120", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 84.9522 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1121.json b/tasks_gen/train/_logs/G1121.json new file mode 100644 index 0000000..a3d47e0 --- /dev/null +++ b/tasks_gen/train/_logs/G1121.json @@ -0,0 +1,36 @@ +{ + "id": "G1121", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/harvest.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/harvest.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 88.4278 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1122.json b/tasks_gen/train/_logs/G1122.json new file mode 100644 index 0000000..9bd95f8 --- /dev/null +++ b/tasks_gen/train/_logs/G1122.json @@ -0,0 +1,59 @@ +{ + "id": "G1122", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/porta_bill_old.prog.abap line 39: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1090 (similarity spec 0.57, rules 0.64, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.60, rules 0.61, names 0.50). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 2 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/dentlab_old.prog.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/dentlab_old.prog.abap line 151: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 151: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "reference/dentlab_old.prog.abap line 157: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 157: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "reference/dentlab_old.prog.abap line 163: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/dentlab_old.prog.abap line 163: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "Too close to task G1090 (similarity spec 0.61, rules 0.65, names 0.25). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.60, rules 0.60, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 7, + "extra": 2 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/rescue_log_old.prog.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "Too close to task G0142 (similarity spec 0.56, rules 0.64, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1090 (similarity spec 0.55, rules 0.66, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1114 (similarity spec 0.53, rules 0.60, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 3 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 88.5286 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1123.json b/tasks_gen/train/_logs/G1123.json new file mode 100644 index 0000000..b199efd --- /dev/null +++ b/tasks_gen/train/_logs/G1123.json @@ -0,0 +1,40 @@ +{ + "id": "G1123", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 89.2, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 8619 (char 8618)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": "named-type", + "spent_total": 87.0034 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1124.json b/tasks_gen/train/_logs/G1124.json new file mode 100644 index 0000000..4896168 --- /dev/null +++ b/tasks_gen/train/_logs/G1124.json @@ -0,0 +1,26 @@ +{ + "id": "G1124", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": "reserved-word", + "spent_total": 88.9796 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1125.json b/tasks_gen/train/_logs/G1125.json new file mode 100644 index 0000000..c11be01 --- /dev/null +++ b/tasks_gen/train/_logs/G1125.json @@ -0,0 +1,26 @@ +{ + "id": "G1125", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 88.9796 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1126.json b/tasks_gen/train/_logs/G1126.json new file mode 100644 index 0000000..ebde51a --- /dev/null +++ b/tasks_gen/train/_logs/G1126.json @@ -0,0 +1,39 @@ +{ + "id": "G1126", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/vet_seed.clas.abap line 9: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"INSERT\"", + "hidden/vet_hidden.clas.abap line 48: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"ASSIGN\"", + "hidden/vet_hidden.clas.abap line 49: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"ASSIGN\"", + "hidden/vet_hidden.clas.abap line 51: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"rv_value\"", + "hidden/vet_hidden.clas.abap line 79: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"" + ], + "by_check": { + "abaplint": 5 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 80.5, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 87.5261 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1127.json b/tasks_gen/train/_logs/G1127.json new file mode 100644 index 0000000..695e2a2 --- /dev/null +++ b/tasks_gen/train/_logs/G1127.json @@ -0,0 +1,16 @@ +{ + "id": "G1127", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 89.059 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1128.json b/tasks_gen/train/_logs/G1128.json new file mode 100644 index 0000000..77eecc2 --- /dev/null +++ b/tasks_gen/train/_logs/G1128.json @@ -0,0 +1,39 @@ +{ + "id": "G1128", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 92.5476 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1129.json b/tasks_gen/train/_logs/G1129.json new file mode 100644 index 0000000..83b1e9f --- /dev/null +++ b/tasks_gen/train/_logs/G1129.json @@ -0,0 +1,40 @@ +{ + "id": "G1129", + "pool": "train", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "seed/mealbk.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/mealbk.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 141: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 1 + } + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 88.5764 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1130.json b/tasks_gen/train/_logs/G1130.json new file mode 100644 index 0000000..e4dab72 --- /dev/null +++ b/tasks_gen/train/_logs/G1130.json @@ -0,0 +1,21 @@ +{ + "id": "G1130", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 89.2332 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1131.json b/tasks_gen/train/_logs/G1131.json new file mode 100644 index 0000000..e41a969 --- /dev/null +++ b/tasks_gen/train/_logs/G1131.json @@ -0,0 +1,21 @@ +{ + "id": "G1131", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 92.825 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1132.json b/tasks_gen/train/_logs/G1132.json new file mode 100644 index 0000000..85a5853 --- /dev/null +++ b/tasks_gen/train/_logs/G1132.json @@ -0,0 +1,35 @@ +{ + "id": "G1132", + "pool": "train", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0160 (similarity spec 0.61, rules 0.43, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 89.7733 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1133.json b/tasks_gen/train/_logs/G1133.json new file mode 100644 index 0000000..1dcaaaa --- /dev/null +++ b/tasks_gen/train/_logs/G1133.json @@ -0,0 +1,36 @@ +{ + "id": "G1133", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 86.8, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 95.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 89.8112 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1134.json b/tasks_gen/train/_logs/G1134.json new file mode 100644 index 0000000..ba0f746 --- /dev/null +++ b/tasks_gen/train/_logs/G1134.json @@ -0,0 +1,52 @@ +{ + "id": "G1134", + "pool": "train", + "object_type": "PROG", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0141 (similarity spec 0.55, rules 0.60, names 0.25). Choose a different business topic and different object names.", + "Too close to task G1090 (similarity spec 0.54, rules 0.61, names 0.20). Choose a different business topic and different object names.", + "Too close to task G1102 (similarity spec 0.57, rules 0.64, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/flower_settle_old.prog.abap line 104: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"go_env\"", + "reference/flower_settle_old.prog.abap line 110: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"go_env\"", + "Too close to task G1102 (similarity spec 0.62, rules 0.69, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 2, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Unterminated string starting at: line 1 column 8475 (char 8474)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G1102 (similarity spec 0.48, rules 0.60, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 93.8384 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1135.json b/tasks_gen/train/_logs/G1135.json new file mode 100644 index 0000000..02d59ab --- /dev/null +++ b/tasks_gen/train/_logs/G1135.json @@ -0,0 +1,21 @@ +{ + "id": "G1135", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": "named-type", + "spent_total": 90.0211 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1136.json b/tasks_gen/train/_logs/G1136.json new file mode 100644 index 0000000..09036a9 --- /dev/null +++ b/tasks_gen/train/_logs/G1136.json @@ -0,0 +1,31 @@ +{ + "id": "G1136", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": "reserved-word", + "spent_total": 91.7429 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1137.json b/tasks_gen/train/_logs/G1137.json new file mode 100644 index 0000000..f5c0c44 --- /dev/null +++ b/tasks_gen/train/_logs/G1137.json @@ -0,0 +1,30 @@ +{ + "id": "G1137", + "pool": "train", + "object_type": "FUNC", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0156 (similarity spec 0.71, rules 0.64, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 93.9376 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1138.json b/tasks_gen/train/_logs/G1138.json new file mode 100644 index 0000000..857e7d2 --- /dev/null +++ b/tasks_gen/train/_logs/G1138.json @@ -0,0 +1,26 @@ +{ + "id": "G1138", + "pool": "train", + "object_type": "PROG", + "category": "G", + "attempts": [ + { + "stage": "validate", + "oracle": 71.7, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 90.1769 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1139.json b/tasks_gen/train/_logs/G1139.json new file mode 100644 index 0000000..7f8e277 --- /dev/null +++ b/tasks_gen/train/_logs/G1139.json @@ -0,0 +1,25 @@ +{ + "id": "G1139", + "pool": "train", + "object_type": "FUNC", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0173 (similarity spec 0.64, rules 0.35, names 1.00). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 91.9096 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1141.json b/tasks_gen/train/_logs/G1141.json new file mode 100644 index 0000000..2ddc88e --- /dev/null +++ b/tasks_gen/train/_logs/G1141.json @@ -0,0 +1,26 @@ +{ + "id": "G1141", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 91.1, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 91.0995 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1142.json b/tasks_gen/train/_logs/G1142.json new file mode 100644 index 0000000..edb2ffd --- /dev/null +++ b/tasks_gen/train/_logs/G1142.json @@ -0,0 +1,40 @@ +{ + "id": "G1142", + "pool": "train", + "object_type": "FUNC", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/hut_fee_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/hut_fee_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"calc_fee\"", + "hidden/hut_fee_hidden.clas.abap line 116: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"calc_fee\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 81.7, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 92.7051 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1144.json b/tasks_gen/train/_logs/G1144.json new file mode 100644 index 0000000..d11a87b --- /dev/null +++ b/tasks_gen/train/_logs/G1144.json @@ -0,0 +1,26 @@ +{ + "id": "G1144", + "pool": "train", + "object_type": "FUNC", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": "reserved-word", + "spent_total": 91.4359 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1145.json b/tasks_gen/train/_logs/G1145.json new file mode 100644 index 0000000..0cf26f6 --- /dev/null +++ b/tasks_gen/train/_logs/G1145.json @@ -0,0 +1,30 @@ +{ + "id": "G1145", + "pool": "train", + "object_type": "DDLS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0175 (similarity spec 0.73, rules 0.65, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 93.2337 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1147.json b/tasks_gen/train/_logs/G1147.json new file mode 100644 index 0000000..7cb5371 --- /dev/null +++ b/tasks_gen/train/_logs/G1147.json @@ -0,0 +1,16 @@ +{ + "id": "G1147", + "pool": "train", + "object_type": "PROG", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 91.5544 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1148.json b/tasks_gen/train/_logs/G1148.json new file mode 100644 index 0000000..6f69f73 --- /dev/null +++ b/tasks_gen/train/_logs/G1148.json @@ -0,0 +1,46 @@ +{ + "id": "G1148", + "pool": "train", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "hidden/t15_hidden.clas.abap line 81: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 83: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 113: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 115: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t15_hidden.clas.abap line 119: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"", + "hidden/t15_hidden.clas.abap line 128: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t15_hidden.clas.abap line 130: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"" + ], + "by_check": { + "abaplint": 12 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 94.4757 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1150.json b/tasks_gen/train/_logs/G1150.json new file mode 100644 index 0000000..5be37d4 --- /dev/null +++ b/tasks_gen/train/_logs/G1150.json @@ -0,0 +1,67 @@ +{ + "id": "G1150", + "pool": "train", + "object_type": "FUNC", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t14_test.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/t14_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/t14_test.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"1\"", + "reference/t14_test.clas.abap line 62: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/t14_test.clas.abap line 62: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t14_hidden.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "hidden/t14_hidden.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/t14_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"1\"", + "hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"6\"", + "hidden/t14_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 100: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "Too close to task G0144 (similarity spec 0.72, rules 0.67, names 0.00). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 15, + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "Too close to task G0144 (similarity spec 0.77, rules 0.72, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/farrier_test.clas.abap line 75: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_visit\"", + "reference/farrier_test.clas.abap line 75: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"", + "hidden/farrier_hidden.clas.abap line 138: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_visit\"", + "hidden/farrier_hidden.clas.abap line 138: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"5\"" + ], + "by_check": { + "abaplint": 4 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 92.1546 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1153.json b/tasks_gen/train/_logs/G1153.json new file mode 100644 index 0000000..a33abd4 --- /dev/null +++ b/tasks_gen/train/_logs/G1153.json @@ -0,0 +1,35 @@ +{ + "id": "G1153", + "pool": "train", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Expecting value: line 1 column 1 (char 0)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "validate", + "oracle": 75.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 93.3, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 94.4723 +} \ No newline at end of file diff --git a/train/STATE.md b/train/STATE.md index 0c156d1..0189b19 100644 --- a/train/STATE.md +++ b/train/STATE.md @@ -148,3 +148,14 @@ Training runs on HF Jobs with Unsloth, not on the Mac. No `mlx_lm` training. - 2026-10-05 15:55 budget correction 2: panel 34.79 at ledger 85.45. Ledger per usage is not constant: 2.04 (24.63 to 31.48, mostly generation) and 1.35 (31.48 to 34.79, mostly trajectory runs). Trajectory runs cost more real usage per ledger USD. Guard set from the panel: panel left 60 - 34.79 - 3.0 reserve = 22.2 usage x 1.35 = 30 ledger, so guard 115 ledger, `BUDGET_LIMIT_USD` 123 (reserve 8). Rule: Kral gives the panel value now and then; the limit is recomputed from it. - 2026-10-05 17:50 budget correction 3: panel 41.07 at ledger 93.45; since 34.79 (85.45): 8.0 ledger / 6.28 usage = 1.27 (trajectory phase). Guard recomputed with ratio 1.2: panel left 60 - 41.07 - 3.0 reserve = 15.9 usage x 1.2 = 19 ledger, guard 112.6, `BUDGET_LIMIT_USD` 121. Burn about 3.7 usage per hour: the guard is reached around 22:00 on 2026-10-05. + +### Stage 2 summary at 50 accepted trajectories (2026-10-05 18:02) + +- Tasks: 139 accepted of 166 generated (K variants 18); trajectory runs 59, accepted trajectories 50 (acceptance 85%). +- Budget: used 10.2 usage (ledger 27.5) since 2026-10-05; left to the guard 14.6 usage (ledger 39.5; reserve 8 ledger kept); allowance until 11 October 24.8 usage. +- Repair share (accepted trajectories with an error followed by a fix): 32/50 = 64%. +- By category, accepted/runs: A 7/7 (repair 4); B 5/8 (repair 5); C 7/8 (repair 6); D 6/6 (repair 3); E 9/10 (repair 6); F 6/6 (repair 2); G 6/6 (repair 5); H 3/5 (repair 0); I 1/3 (repair 1). +- By object type, accepted/runs: CLAS 38/42 (repair 21); DDLS 6/11 (repair 6); FUNC 6/6 (repair 5). +- Tokens of accepted samples (20 tool schemas kept): p50 20147, p90 39458, p95 47694, max 71791, n 50. +- Syntax hints (proxy syntaxCheck added): 1 in 1 runs. +- Harness events: 2 ({'text:[LOCK]': 1, 'teardown': 1}); trajectory workers now 1.