From 13360292dd5fcdf4d4d18ec3a26f627866899d80 Mon Sep 17 00:00:00 2001 From: Kral Date: Mon, 5 Oct 2026 13:35:54 +0200 Subject: [PATCH] Pipeline controller: plan 2 (hard +, error +50 %), backlog throttle, 2 trajectory workers, stop rules, summaries every 50; budget reserve Co-Authored-By: Claude Sonnet 5.5 --- docs/yol-haritasi.md | 2 + harness/ledger.py | 3 +- harness/pipeline.py | 353 ++++ harness/trainset.py | 89 +- harness/trajectories.py | 2 +- .../G1000/faulty/m0_i_cont_weight.ddls.asddls | 18 + .../G1000/faulty/m1_i_cont_weight.ddls.asddls | 18 + .../G1000/faulty/m2_i_cont_weight.ddls.asddls | 18 + .../G1000/faulty/m3_i_cont_weight.ddls.asddls | 18 + .../G1000/faulty/m4_i_cont_weight.ddls.asddls | 18 + tasks_gen/train/G1000/generation.json | 19 + .../train/G1000/hidden/t20_hidden.clas.abap | 108 ++ tasks_gen/train/G1000/mutation.json | 65 + .../G1000/reference/i_cont_weight.ddls.asddls | 18 + .../train/G1000/reference/t20_test.clas.abap | 53 + tasks_gen/train/G1000/seed/cont.tabl.asabap | 12 + tasks_gen/train/G1000/seed/weigh.tabl.asabap | 10 + tasks_gen/train/G1000/spec.md | 41 + tasks_gen/train/G1000/task.json | 68 + .../faulty/m0_harvest_evaluator.clas.abap | 71 + .../faulty/m2_harvest_evaluator.clas.abap | 71 + .../faulty/m3_harvest_evaluator.clas.abap | 71 + tasks_gen/train/G1001/generation.json | 19 + .../G1001/hidden/harvest_hidden.clas.abap | 193 ++ tasks_gen/train/G1001/mutation.json | 66 + .../reference/harvest_evaluator.clas.abap | 71 + .../harvest_evaluator.testclasses.abap | 84 + .../train/G1001/seed/if_harvest.intf.abap | 26 + tasks_gen/train/G1001/spec.md | 52 + tasks_gen/train/G1001/task.json | 53 + .../G1002/faulty/m0_glasses_pricer.clas.abap | 109 ++ .../G1002/faulty/m1_glasses_pricer.clas.abap | 109 ++ .../G1002/faulty/m2_glasses_pricer.clas.abap | 109 ++ .../G1002/faulty/m3_glasses_pricer.clas.abap | 109 ++ .../G1002/faulty/m4_glasses_pricer.clas.abap | 109 ++ tasks_gen/train/G1002/generation.json | 24 + .../G1002/hidden/glasses_hidden.clas.abap | 100 ++ tasks_gen/train/G1002/mutation.json | 71 + .../G1002/reference/glasses_pricer.clas.abap | 109 ++ .../reference/glasses_pricer.testclasses.abap | 83 + .../train/G1002/seed/glasses_pricer.clas.abap | 75 + .../G1002/seed/optic_quote_rpt.prog.abap | 12 + tasks_gen/train/G1002/spec.md | 51 + tasks_gen/train/G1002/task.json | 57 + .../G1003/faulty/m0_drone_checker.clas.abap | 49 + .../G1003/faulty/m1_drone_checker.clas.abap | 49 + .../G1003/faulty/m2_drone_checker.clas.abap | 49 + .../G1003/faulty/m3_drone_checker.clas.abap | 49 + tasks_gen/train/G1003/generation.json | 24 + .../train/G1003/hidden/t21_hidden.clas.abap | 172 ++ tasks_gen/train/G1003/mutation.json | 65 + .../G1003/reference/drone_checker.clas.abap | 49 + .../reference/drone_checker.testclasses.abap | 110 ++ .../train/G1003/seed/if_drone_check.intf.abap | 25 + tasks_gen/train/G1003/spec.md | 65 + tasks_gen/train/G1003/task.json | 51 + .../G1004/faulty/m0_balloon_checker.clas.abap | 52 + .../G1004/faulty/m1_balloon_checker.clas.abap | 52 + .../G1004/faulty/m2_balloon_checker.clas.abap | 52 + .../G1004/faulty/m3_balloon_checker.clas.abap | 52 + .../G1004/faulty/m4_balloon_checker.clas.abap | 52 + tasks_gen/train/G1004/generation.json | 24 + .../G1004/hidden/balloon_hidden.clas.abap | 169 ++ tasks_gen/train/G1004/mutation.json | 80 + .../G1004/reference/balloon_checker.clas.abap | 52 + .../balloon_checker.testclasses.abap | 89 + .../reference/cx_balloon_cloud.clas.abap | 16 + .../G1004/reference/cx_balloon_crew.clas.abap | 16 + .../reference/cx_balloon_weight.clas.abap | 16 + .../G1004/reference/cx_balloon_wind.clas.abap | 16 + .../G1004/seed/cx_balloon_flight.clas.abap | 18 + tasks_gen/train/G1004/spec.md | 76 + tasks_gen/train/G1004/task.json | 90 + .../G1005/faulty/m0_credit_checker.clas.abap | 59 + .../G1005/faulty/m2_credit_checker.clas.abap | 59 + .../G1005/faulty/m3_credit_checker.clas.abap | 59 + .../G1005/faulty/m4_credit_checker.clas.abap | 59 + tasks_gen/train/G1005/generation.json | 24 + .../train/G1005/hidden/t02_hidden.clas.abap | 129 ++ tasks_gen/train/G1005/mutation.json | 73 + .../G1005/reference/credit_checker.clas.abap | 59 + .../reference/credit_checker.testclasses.abap | 98 + .../G1005/seed/if_credit_check.intf.abap | 18 + tasks_gen/train/G1005/spec.md | 56 + tasks_gen/train/G1005/task.json | 51 + .../G1006/faulty/m0_auc_premium.func.abap | 41 + .../G1006/faulty/m1_auc_premium.func.abap | 41 + .../G1006/faulty/m2_auc_premium.func.abap | 41 + .../G1006/faulty/m3_auc_premium.func.abap | 41 + .../G1006/faulty/m4_auc_premium.func.abap | 41 + tasks_gen/train/G1006/generation.json | 60 + .../train/G1006/hidden/t14_hidden.clas.abap | 133 ++ tasks_gen/train/G1006/mutation.json | 60 + .../G1006/reference/auc_premium.func.abap | 41 + .../train/G1006/reference/t14_test.clas.abap | 99 + .../train/G1006/seed/auc_sample.func.abap | 15 + tasks_gen/train/G1006/spec.md | 47 + tasks_gen/train/G1006/task.json | 84 + tasks_gen/train/G1007/generation.json | 14 + .../G1007/reference/jump_pricer.clas.abap | 56 + .../reference/jump_pricer.testclasses.abap | 61 + tasks_gen/train/G1007/spec.md | 50 + tasks_gen/train/G1007/task.json | 54 + tasks_gen/train/G1008/generation.json | 33 + .../train/G1008/hidden/turb_hidden.clas.abap | 103 ++ tasks_gen/train/G1008/mutation.json | 42 + .../G1008/reference/i_turbine.ddls.asddls | 14 + .../train/G1008/reference/turb_test.clas.abap | 41 + .../train/G1008/seed/turbine.tabl.asabap | 12 + tasks_gen/train/G1008/seed/wpark.tabl.asabap | 12 + tasks_gen/train/G1008/spec.md | 47 + tasks_gen/train/G1008/task.json | 66 + .../G1009/faulty/m0_fish_auction.clas.abap | 96 + .../G1009/faulty/m1_fish_auction.clas.abap | 96 + .../G1009/faulty/m2_fish_auction.clas.abap | 96 + .../G1009/faulty/m3_fish_auction.clas.abap | 96 + .../G1009/faulty/m4_fish_auction.clas.abap | 96 + tasks_gen/train/G1009/generation.json | 29 + .../train/G1009/hidden/t31_hidden.clas.abap | 140 ++ tasks_gen/train/G1009/mutation.json | 74 + .../G1009/reference/fish_auction.clas.abap | 96 + .../reference/fish_auction.testclasses.abap | 77 + tasks_gen/train/G1009/spec.md | 74 + tasks_gen/train/G1009/task.json | 43 + .../faulty/m0_phone_repair_pricer.clas.abap | 105 ++ .../faulty/m1_phone_repair_pricer.clas.abap | 105 ++ .../faulty/m2_phone_repair_pricer.clas.abap | 105 ++ .../faulty/m3_phone_repair_pricer.clas.abap | 105 ++ .../faulty/m4_phone_repair_pricer.clas.abap | 105 ++ tasks_gen/train/G1010/generation.json | 28 + .../train/G1010/hidden/t31_hidden.clas.abap | 137 ++ tasks_gen/train/G1010/mutation.json | 79 + .../reference/phone_repair_pricer.clas.abap | 105 ++ .../phone_repair_pricer.testclasses.abap | 84 + .../G1010/seed/phone_repair_pricer.clas.abap | 90 + tasks_gen/train/G1010/spec.md | 55 + tasks_gen/train/G1010/task.json | 49 + .../faulty/m1_i_repair_charge.ddls.asddls | 15 + .../faulty/m2_i_repair_charge.ddls.asddls | 15 + .../faulty/m3_i_repair_charge.ddls.asddls | 15 + .../faulty/m4_i_repair_charge.ddls.asddls | 15 + tasks_gen/train/G1011/generation.json | 19 + .../hidden/repair_charge_hidden.clas.abap | 97 + tasks_gen/train/G1011/mutation.json | 69 + .../reference/i_repair_charge.ddls.asddls | 15 + .../reference/repair_charge_test.clas.abap | 59 + .../G1011/seed/i_repair_charge.ddls.asddls | 14 + tasks_gen/train/G1011/seed/job.tabl.asabap | 14 + tasks_gen/train/G1011/seed/rate.tabl.asabap | 11 + .../train/G1011/seed/seed_repair.clas.abap | 34 + tasks_gen/train/G1011/spec.md | 47 + tasks_gen/train/G1011/task.json | 78 + .../G1012/faulty/m0_i_milk_total.ddls.asddls | 16 + .../G1012/faulty/m1_i_milk_total.ddls.asddls | 16 + .../G1012/faulty/m2_i_milk_total.ddls.asddls | 16 + .../G1012/faulty/m3_i_milk_total.ddls.asddls | 16 + .../G1012/faulty/m4_i_milk_total.ddls.asddls | 16 + tasks_gen/train/G1012/generation.json | 29 + .../train/G1012/hidden/t16_hidden.clas.abap | 99 + tasks_gen/train/G1012/mutation.json | 66 + .../G1012/reference/i_milk_total.ddls.asddls | 16 + .../train/G1012/reference/t16_test.clas.abap | 66 + tasks_gen/train/G1012/seed/farm.tabl.asabap | 11 + tasks_gen/train/G1012/seed/milk.tabl.asabap | 11 + tasks_gen/train/G1012/spec.md | 40 + tasks_gen/train/G1012/task.json | 65 + .../G1013/faulty/m0_log_merger.clas.abap | 84 + .../G1013/faulty/m1_log_merger.clas.abap | 84 + .../G1013/faulty/m2_log_merger.clas.abap | 84 + .../G1013/faulty/m3_log_merger.clas.abap | 84 + .../G1013/faulty/m4_log_merger.clas.abap | 84 + tasks_gen/train/G1013/generation.json | 19 + .../train/G1013/hidden/t20_hidden.clas.abap | 138 ++ tasks_gen/train/G1013/mutation.json | 77 + .../G1013/reference/log_merger.clas.abap | 84 + .../reference/log_merger.testclasses.abap | 71 + tasks_gen/train/G1013/spec.md | 63 + tasks_gen/train/G1013/task.json | 42 + .../G1014/faulty/m0_frame_pricer.clas.abap | 189 ++ .../G1014/faulty/m1_frame_pricer.clas.abap | 189 ++ .../G1014/faulty/m2_frame_pricer.clas.abap | 189 ++ .../G1014/faulty/m3_frame_pricer.clas.abap | 189 ++ .../G1014/faulty/m4_frame_pricer.clas.abap | 189 ++ tasks_gen/train/G1014/generation.json | 52 + .../hidden/frame_pricer_hidden.clas.abap | 171 ++ tasks_gen/train/G1014/mutation.json | 79 + .../G1014/reference/frame_pricer.clas.abap | 189 ++ .../reference/frame_pricer.testclasses.abap | 87 + .../train/G1014/seed/frame_pricer.clas.abap | 127 ++ tasks_gen/train/G1014/spec.md | 48 + tasks_gen/train/G1014/task.json | 48 + .../G1015/faulty/m0_towing_fee.clas.abap | 133 ++ .../G1015/faulty/m1_towing_fee.clas.abap | 133 ++ .../G1015/faulty/m2_towing_fee.clas.abap | 133 ++ .../G1015/faulty/m3_towing_fee.clas.abap | 133 ++ .../G1015/faulty/m4_towing_fee.clas.abap | 133 ++ tasks_gen/train/G1015/generation.json | 19 + .../train/G1015/hidden/t02_hidden.clas.abap | 169 ++ tasks_gen/train/G1015/mutation.json | 75 + .../G1015/reference/towing_fee.clas.abap | 133 ++ .../reference/towing_fee.testclasses.abap | 74 + tasks_gen/train/G1015/spec.md | 67 + tasks_gen/train/G1015/task.json | 41 + tasks_gen/train/G1016/generation.json | 35 + .../train/G1016/hidden/t02_hidden.clas.abap | 143 ++ .../G1016/reference/cx_return_error.clas.abap | 28 + .../G1016/reference/return_checker.clas.abap | 86 + .../reference/return_checker.testclasses.abap | 99 + .../train/G1016/seed/seed_vehicle.clas.abap | 16 + tasks_gen/train/G1016/seed/vehicle.tabl.abap | 10 + tasks_gen/train/G1016/spec.md | 72 + tasks_gen/train/G1016/task.json | 68 + .../faulty/m0_inspection_checker.clas.abap | 67 + .../faulty/m2_inspection_checker.clas.abap | 67 + .../faulty/m3_inspection_checker.clas.abap | 67 + tasks_gen/train/G1017/generation.json | 19 + .../train/G1017/hidden/t_f01_hidden.clas.abap | 119 ++ tasks_gen/train/G1017/mutation.json | 61 + .../reference/inspection_checker.clas.abap | 67 + .../inspection_checker.testclasses.abap | 84 + .../G1017/seed/if_inspection_check.intf.abap | 25 + tasks_gen/train/G1017/spec.md | 50 + tasks_gen/train/G1017/task.json | 51 + .../train/G1018/faulty/m0_ad_price.func.abap | 48 + .../train/G1018/faulty/m1_ad_price.func.abap | 48 + .../train/G1018/faulty/m2_ad_price.func.abap | 48 + .../train/G1018/faulty/m3_ad_price.func.abap | 48 + .../train/G1018/faulty/m4_ad_price.func.abap | 48 + tasks_gen/train/G1018/generation.json | 52 + .../train/G1018/hidden/ad_hidden.clas.abap | 125 ++ tasks_gen/train/G1018/mutation.json | 78 + .../train/G1018/reference/ad_price.func.abap | 48 + .../train/G1018/reference/ad_test.clas.abap | 92 + tasks_gen/train/G1018/spec.md | 47 + tasks_gen/train/G1018/task.json | 76 + tasks_gen/train/G1019/generation.json | 23 + .../G1019/reference/bowling_fee.clas.abap | 60 + .../reference/bowling_fee.testclasses.abap | 72 + .../train/G1019/seed/if_bowling_fee.intf.abap | 8 + tasks_gen/train/G1019/spec.md | 45 + tasks_gen/train/G1019/task.json | 61 + .../G1020/faulty/m0_i_plant_grade.ddls.asddls | 21 + .../G1020/faulty/m1_i_plant_grade.ddls.asddls | 21 + .../G1020/faulty/m3_i_plant_grade.ddls.asddls | 21 + .../G1020/faulty/m4_i_plant_grade.ddls.asddls | 21 + tasks_gen/train/G1020/generation.json | 29 + .../train/G1020/hidden/t33_hidden.clas.abap | 86 + tasks_gen/train/G1020/mutation.json | 53 + .../G1020/reference/i_plant_grade.ddls.asddls | 21 + .../train/G1020/reference/t33_test.clas.abap | 43 + tasks_gen/train/G1020/seed/plant.tabl.asabap | 11 + tasks_gen/train/G1020/spec.md | 39 + tasks_gen/train/G1020/task.json | 59 + tasks_gen/train/G1021/generation.json | 29 + .../train/G1021/hidden/t41_hidden.clas.abap | 235 +++ .../G1021/reference/roast_merger.clas.abap | 88 + .../reference/roast_merger.testclasses.abap | 147 ++ tasks_gen/train/G1021/spec.md | 65 + tasks_gen/train/G1021/task.json | 49 + .../G1022/faulty/m0_sweep_pricer.clas.abap | 106 ++ .../G1022/faulty/m1_sweep_pricer.clas.abap | 106 ++ .../G1022/faulty/m2_sweep_pricer.clas.abap | 106 ++ .../G1022/faulty/m3_sweep_pricer.clas.abap | 106 ++ .../G1022/faulty/m4_sweep_pricer.clas.abap | 106 ++ tasks_gen/train/G1022/generation.json | 19 + .../train/G1022/hidden/sweep_hidden.clas.abap | 133 ++ tasks_gen/train/G1022/mutation.json | 84 + .../G1022/reference/sweep_pricer.clas.abap | 106 ++ .../reference/sweep_pricer.testclasses.abap | 81 + .../train/G1022/seed/sweep_pricer.clas.abap | 98 + tasks_gen/train/G1022/spec.md | 42 + tasks_gen/train/G1022/task.json | 49 + .../G1023/faulty/m0_rideshare_split.clas.abap | 66 + .../G1023/faulty/m1_rideshare_split.clas.abap | 66 + .../G1023/faulty/m3_rideshare_split.clas.abap | 66 + .../G1023/faulty/m4_rideshare_split.clas.abap | 66 + tasks_gen/train/G1023/generation.json | 24 + .../train/G1023/hidden/t21_hidden.clas.abap | 93 + tasks_gen/train/G1023/mutation.json | 71 + .../G1023/reference/rideshare_split.clas.abap | 66 + .../rideshare_split.testclasses.abap | 66 + tasks_gen/train/G1023/spec.md | 59 + tasks_gen/train/G1023/task.json | 47 + .../G1024/faulty/m0_holiday_checker.clas.abap | 109 ++ .../G1024/faulty/m1_holiday_checker.clas.abap | 109 ++ .../G1024/faulty/m2_holiday_checker.clas.abap | 109 ++ .../G1024/faulty/m3_holiday_checker.clas.abap | 109 ++ .../G1024/faulty/m4_holiday_checker.clas.abap | 109 ++ tasks_gen/train/G1024/generation.json | 24 + .../G1024/hidden/holiday_hidden.clas.abap | 184 ++ tasks_gen/train/G1024/mutation.json | 69 + .../reference/cx_bad_departure.clas.abap | 25 + .../G1024/reference/cx_bad_nights.clas.abap | 22 + .../reference/cx_bad_travellers.clas.abap | 25 + .../G1024/reference/cx_unknown_dest.clas.abap | 22 + .../G1024/reference/holiday_checker.clas.abap | 109 ++ .../holiday_checker.testclasses.abap | 115 ++ .../G1024/seed/cx_holiday_error.clas.abap | 22 + tasks_gen/train/G1024/spec.md | 97 + tasks_gen/train/G1024/task.json | 94 + .../G1025/faulty/m0_crane_hire_calc.clas.abap | 124 ++ .../G1025/faulty/m1_crane_hire_calc.clas.abap | 124 ++ .../G1025/faulty/m2_crane_hire_calc.clas.abap | 124 ++ .../G1025/faulty/m4_crane_hire_calc.clas.abap | 124 ++ tasks_gen/train/G1025/generation.json | 38 + .../train/G1025/hidden/t02_hidden.clas.abap | 234 +++ tasks_gen/train/G1025/mutation.json | 73 + .../G1025/reference/crane_hire_calc.clas.abap | 124 ++ .../crane_hire_calc.testclasses.abap | 104 ++ .../train/G1025/seed/if_crane_hire.intf.abap | 25 + tasks_gen/train/G1025/spec.md | 54 + tasks_gen/train/G1025/task.json | 51 + .../train/G1026/faulty/m0_guard_fee.func.abap | 45 + .../train/G1026/faulty/m1_guard_fee.func.abap | 45 + .../train/G1026/faulty/m2_guard_fee.func.abap | 45 + .../train/G1026/faulty/m3_guard_fee.func.abap | 45 + .../train/G1026/faulty/m4_guard_fee.func.abap | 45 + tasks_gen/train/G1026/generation.json | 28 + .../train/G1026/hidden/t88_hidden.clas.abap | 129 ++ tasks_gen/train/G1026/mutation.json | 71 + .../train/G1026/reference/guard_fee.func.abap | 45 + .../train/G1026/reference/t88_test.clas.abap | 68 + .../train/G1026/seed/guard_sample.func.abap | 14 + tasks_gen/train/G1026/spec.md | 43 + tasks_gen/train/G1026/task.json | 81 + tasks_gen/train/G1027/generation.json | 30 + .../G1027/reference/scaffold_fee.clas.abap | 54 + .../reference/scaffold_fee.testclasses.abap | 52 + .../train/G1027/seed/if_hire_fee.intf.abap | 13 + tasks_gen/train/G1027/spec.md | 51 + tasks_gen/train/G1027/task.json | 61 + tasks_gen/train/G1028/generation.json | 50 + .../train/G1028/hidden/t20_hidden.clas.abap | 73 + tasks_gen/train/G1028/mutation.json | 39 + .../train/G1028/reference/i_dept.ddls.asddls | 12 + .../train/G1028/reference/t20_test.clas.abap | 35 + tasks_gen/train/G1028/seed/dept.tabl.asabap | 12 + .../train/G1028/seed/seed_dept.clas.abap | 20 + tasks_gen/train/G1028/spec.md | 46 + tasks_gen/train/G1028/task.json | 67 + .../G1029/faulty/m0_licence_merger.clas.abap | 77 + .../G1029/faulty/m1_licence_merger.clas.abap | 77 + .../G1029/faulty/m2_licence_merger.clas.abap | 77 + .../G1029/faulty/m3_licence_merger.clas.abap | 77 + .../G1029/faulty/m4_licence_merger.clas.abap | 77 + tasks_gen/train/G1029/generation.json | 29 + .../train/G1029/hidden/t_lic_hidden.clas.abap | 197 ++ tasks_gen/train/G1029/mutation.json | 77 + .../G1029/reference/licence_merger.clas.abap | 77 + .../reference/licence_merger.testclasses.abap | 92 + .../G1029/seed/if_licence_merge.intf.abap | 18 + tasks_gen/train/G1029/spec.md | 56 + tasks_gen/train/G1029/task.json | 52 + .../G1030/faulty/m0_skip_hire_calc.clas.abap | 174 ++ .../G1030/faulty/m1_skip_hire_calc.clas.abap | 174 ++ .../G1030/faulty/m2_skip_hire_calc.clas.abap | 174 ++ .../G1030/faulty/m4_skip_hire_calc.clas.abap | 174 ++ tasks_gen/train/G1030/generation.json | 36 + .../train/G1030/hidden/t61_hidden.clas.abap | 155 ++ tasks_gen/train/G1030/mutation.json | 76 + .../G1030/reference/skip_hire_calc.clas.abap | 174 ++ .../reference/skip_hire_calc.testclasses.abap | 65 + .../train/G1030/seed/skip_hire_calc.clas.abap | 169 ++ tasks_gen/train/G1030/spec.md | 51 + tasks_gen/train/G1030/task.json | 53 + .../faulty/m0_i_film_revenue.ddls.asddls | 13 + .../faulty/m1_i_film_revenue.ddls.asddls | 13 + .../faulty/m2_i_film_revenue.ddls.asddls | 13 + .../faulty/m3_i_film_revenue.ddls.asddls | 13 + .../faulty/m4_i_film_revenue.ddls.asddls | 13 + tasks_gen/train/G1031/generation.json | 19 + .../G1031/hidden/t_cinema_hidden.clas.abap | 76 + tasks_gen/train/G1031/mutation.json | 65 + .../reference/i_film_revenue.ddls.asddls | 13 + .../G1031/reference/t_cinema_test.clas.abap | 46 + .../G1031/seed/i_film_revenue.ddls.asddls | 15 + tasks_gen/train/G1031/seed/screen.tabl.asabap | 13 + .../train/G1031/seed/seed_cinema.clas.abap | 28 + tasks_gen/train/G1031/seed/ticket.tabl.asabap | 11 + tasks_gen/train/G1031/spec.md | 38 + tasks_gen/train/G1031/task.json | 78 + .../G1032/faulty/m1_i_tutor_load.ddls.asddls | 16 + .../G1032/faulty/m2_i_tutor_load.ddls.asddls | 16 + .../G1032/faulty/m3_i_tutor_load.ddls.asddls | 16 + .../G1032/faulty/m4_i_tutor_load.ddls.asddls | 16 + tasks_gen/train/G1032/generation.json | 28 + .../train/G1032/hidden/t31_hidden.clas.abap | 112 ++ tasks_gen/train/G1032/mutation.json | 71 + .../G1032/reference/i_tutor_load.ddls.asddls | 16 + .../train/G1032/reference/t31_test.clas.abap | 40 + tasks_gen/train/G1032/seed/lesson.tabl.asabap | 12 + tasks_gen/train/G1032/seed/tutor.tabl.asabap | 11 + tasks_gen/train/G1032/spec.md | 39 + tasks_gen/train/G1032/task.json | 65 + .../G1033/faulty/m0_oil_settlement.clas.abap | 73 + .../G1033/faulty/m1_oil_settlement.clas.abap | 73 + .../G1033/faulty/m2_oil_settlement.clas.abap | 73 + .../G1033/faulty/m3_oil_settlement.clas.abap | 73 + .../G1033/faulty/m4_oil_settlement.clas.abap | 73 + tasks_gen/train/G1033/generation.json | 19 + .../train/G1033/hidden/t_oil_hidden.clas.abap | 134 ++ tasks_gen/train/G1033/mutation.json | 70 + .../G1033/reference/oil_settlement.clas.abap | 73 + .../reference/oil_settlement.testclasses.abap | 86 + tasks_gen/train/G1033/spec.md | 68 + tasks_gen/train/G1033/task.json | 42 + .../G1034/faulty/m0_storage_pricer.clas.abap | 108 ++ .../G1034/faulty/m1_storage_pricer.clas.abap | 108 ++ .../G1034/faulty/m2_storage_pricer.clas.abap | 108 ++ .../G1034/faulty/m3_storage_pricer.clas.abap | 108 ++ .../G1034/faulty/m4_storage_pricer.clas.abap | 108 ++ tasks_gen/train/G1034/generation.json | 19 + .../G1034/hidden/t_stor_hidden.clas.abap | 205 +++ tasks_gen/train/G1034/mutation.json | 84 + .../G1034/reference/storage_pricer.clas.abap | 108 ++ .../reference/storage_pricer.testclasses.abap | 92 + .../train/G1034/seed/storage_pricer.clas.abap | 75 + tasks_gen/train/G1034/spec.md | 46 + tasks_gen/train/G1034/task.json | 49 + .../G1035/faulty/m0_photo_pricer.clas.abap | 148 ++ .../G1035/faulty/m1_photo_pricer.clas.abap | 148 ++ .../G1035/faulty/m2_photo_pricer.clas.abap | 148 ++ .../G1035/faulty/m3_photo_pricer.clas.abap | 148 ++ .../G1035/faulty/m4_photo_pricer.clas.abap | 148 ++ tasks_gen/train/G1035/generation.json | 19 + .../train/G1035/hidden/t120_hidden.clas.abap | 267 +++ tasks_gen/train/G1035/mutation.json | 65 + .../G1035/reference/photo_pricer.clas.abap | 148 ++ .../reference/photo_pricer.testclasses.abap | 121 ++ tasks_gen/train/G1035/spec.md | 80 + tasks_gen/train/G1035/task.json | 48 + .../G1036/faulty/m0_ride_booking.clas.abap | 140 ++ .../G1036/faulty/m1_ride_booking.clas.abap | 140 ++ .../G1036/faulty/m2_ride_booking.clas.abap | 140 ++ .../G1036/faulty/m3_ride_booking.clas.abap | 140 ++ .../G1036/faulty/m4_ride_booking.clas.abap | 140 ++ tasks_gen/train/G1036/generation.json | 24 + .../train/G1036/hidden/ride_hidden.clas.abap | 226 +++ tasks_gen/train/G1036/mutation.json | 75 + .../G1036/reference/ride_booking.clas.abap | 140 ++ .../reference/ride_booking.testclasses.abap | 143 ++ .../G1036/seed/cx_ride_booking.clas.abap | 19 + .../train/G1036/seed/cx_ride_input.clas.abap | 7 + .../train/G1036/seed/cx_ride_limit.clas.abap | 7 + .../G1036/seed/if_ride_booking.intf.abap | 43 + tasks_gen/train/G1036/spec.md | 79 + tasks_gen/train/G1036/task.json | 72 + .../G1037/faulty/m0_piano_pricer.clas.abap | 87 + .../G1037/faulty/m1_piano_pricer.clas.abap | 87 + .../G1037/faulty/m2_piano_pricer.clas.abap | 87 + .../G1037/faulty/m3_piano_pricer.clas.abap | 87 + .../G1037/faulty/m4_piano_pricer.clas.abap | 87 + tasks_gen/train/G1037/generation.json | 19 + .../train/G1037/hidden/t42_hidden.clas.abap | 140 ++ tasks_gen/train/G1037/mutation.json | 70 + .../G1037/reference/piano_pricer.clas.abap | 87 + .../reference/piano_pricer.testclasses.abap | 84 + .../train/G1037/seed/tune_rates.clas.abap | 30 + tasks_gen/train/G1037/spec.md | 68 + tasks_gen/train/G1037/task.json | 50 + .../G1038/faulty/m0_mkt_stall_fee.func.abap | 53 + .../G1038/faulty/m1_mkt_stall_fee.func.abap | 53 + .../G1038/faulty/m2_mkt_stall_fee.func.abap | 53 + .../G1038/faulty/m3_mkt_stall_fee.func.abap | 53 + .../G1038/faulty/m4_mkt_stall_fee.func.abap | 53 + tasks_gen/train/G1038/generation.json | 24 + .../train/G1038/hidden/t_mkt_hidden.clas.abap | 99 + tasks_gen/train/G1038/mutation.json | 79 + .../G1038/reference/mkt_fee_test.clas.abap | 89 + .../G1038/reference/mkt_stall_fee.func.abap | 53 + .../train/G1038/seed/mkt_sample.func.abap | 14 + tasks_gen/train/G1038/spec.md | 57 + tasks_gen/train/G1038/task.json | 80 + tasks_gen/train/G1039/generation.json | 14 + .../G1039/reference/tour_summary.clas.abap | 76 + .../reference/tour_summary.testclasses.abap | 102 ++ .../G1039/seed/if_tour_summary.intf.abap | 32 + tasks_gen/train/G1039/spec.md | 52 + tasks_gen/train/G1039/task.json | 60 + .../G1040/faulty/m0_i_roll_grade.ddls.asddls | 20 + .../G1040/faulty/m1_i_roll_grade.ddls.asddls | 20 + .../G1040/faulty/m2_i_roll_grade.ddls.asddls | 20 + .../G1040/faulty/m3_i_roll_grade.ddls.asddls | 20 + .../G1040/faulty/m4_i_roll_grade.ddls.asddls | 20 + tasks_gen/train/G1040/generation.json | 19 + .../train/G1040/hidden/t16_hidden.clas.abap | 97 + tasks_gen/train/G1040/mutation.json | 57 + .../G1040/reference/i_roll_grade.ddls.asddls | 20 + .../train/G1040/reference/t16_test.clas.abap | 46 + tasks_gen/train/G1040/seed/fabric.tabl.asabap | 11 + tasks_gen/train/G1040/seed/roll.tabl.asabap | 13 + tasks_gen/train/G1040/spec.md | 43 + tasks_gen/train/G1040/task.json | 67 + tasks_gen/train/G1041/generation.json | 29 + .../train/G1041/hidden/feed_hidden.clas.abap | 232 +++ .../G1041/reference/feed_stock.clas.abap | 90 + .../reference/feed_stock.testclasses.abap | 85 + .../train/G1041/seed/if_feed_stock.intf.abap | 33 + tasks_gen/train/G1041/spec.md | 76 + tasks_gen/train/G1041/task.json | 52 + .../G1042/hidden/snow_fee_hidden.clas.abap | 90 + .../train/G1042/reference/snow_fee.clas.abap | 86 + .../G1042/reference/snow_fee.testclasses.abap | 59 + tasks_gen/train/G1042/seed/snow_fee.clas.abap | 64 + tasks_gen/train/G1042/spec.md | 52 + tasks_gen/train/G1042/task.json | 48 + .../train/G1043/faulty/m0_med_copay.clas.abap | 93 + .../train/G1043/faulty/m1_med_copay.clas.abap | 93 + .../train/G1043/faulty/m2_med_copay.clas.abap | 93 + .../train/G1043/faulty/m3_med_copay.clas.abap | 93 + .../train/G1043/faulty/m4_med_copay.clas.abap | 93 + tasks_gen/train/G1043/generation.json | 19 + .../train/G1043/hidden/t40_hidden.clas.abap | 100 ++ tasks_gen/train/G1043/mutation.json | 82 + .../train/G1043/reference/med_copay.clas.abap | 93 + .../reference/med_copay.testclasses.abap | 73 + .../train/G1043/seed/if_med_copay.intf.abap | 32 + tasks_gen/train/G1043/spec.md | 58 + tasks_gen/train/G1043/task.json | 51 + .../G1044/faulty/m0_escape_checker.clas.abap | 113 ++ .../G1044/faulty/m1_escape_checker.clas.abap | 113 ++ .../train/G1044/hidden/t02_hidden.clas.abap | 161 ++ .../G1044/reference/escape_checker.clas.abap | 113 ++ .../reference/escape_checker.testclasses.abap | 98 + .../train/G1044/seed/cx_escape_age.clas.abap | 7 + .../G1044/seed/cx_escape_error.clas.abap | 17 + .../G1044/seed/cx_escape_players.clas.abap | 7 + .../train/G1044/seed/cx_escape_room.clas.abap | 7 + .../train/G1044/seed/cx_escape_time.clas.abap | 7 + tasks_gen/train/G1044/spec.md | 72 + tasks_gen/train/G1044/task.json | 78 + .../train/G1046/faulty/m0_tree_fee.func.abap | 58 + .../train/G1046/faulty/m1_tree_fee.func.abap | 58 + .../train/G1046/faulty/m2_tree_fee.func.abap | 58 + .../train/G1046/faulty/m3_tree_fee.func.abap | 58 + .../train/G1046/faulty/m4_tree_fee.func.abap | 58 + tasks_gen/train/G1046/generation.json | 52 + .../train/G1046/hidden/tree_hidden.clas.abap | 128 ++ tasks_gen/train/G1046/mutation.json | 77 + .../train/G1046/reference/tree_fee.func.abap | 58 + .../train/G1046/reference/tree_test.clas.abap | 103 ++ tasks_gen/train/G1046/spec.md | 59 + tasks_gen/train/G1046/task.json | 77 + .../G1049/faulty/m0_quote_merger.clas.abap | 92 + .../G1049/faulty/m1_quote_merger.clas.abap | 92 + .../G1049/faulty/m2_quote_merger.clas.abap | 92 + .../G1049/faulty/m3_quote_merger.clas.abap | 92 + .../G1049/faulty/m4_quote_merger.clas.abap | 92 + tasks_gen/train/G1049/generation.json | 28 + .../hidden/quote_merger_hidden.clas.abap | 249 +++ tasks_gen/train/G1049/mutation.json | 74 + .../G1049/reference/quote_merger.clas.abap | 92 + .../reference/quote_merger.testclasses.abap | 126 ++ tasks_gen/train/G1049/spec.md | 59 + tasks_gen/train/G1049/task.json | 48 + .../G1052/faulty/m1_i_bike_use.ddls.asddls | 16 + .../G1052/faulty/m2_i_bike_use.ddls.asddls | 16 + .../G1052/faulty/m3_i_bike_use.ddls.asddls | 16 + .../G1052/faulty/m4_i_bike_use.ddls.asddls | 16 + tasks_gen/train/G1052/generation.json | 19 + .../train/G1052/hidden/t_bike_hid.clas.abap | 84 + tasks_gen/train/G1052/mutation.json | 64 + .../G1052/reference/i_bike_use.ddls.asddls | 16 + .../G1052/reference/t_bike_test.clas.abap | 47 + tasks_gen/train/G1052/seed/bike.tabl.asabap | 11 + tasks_gen/train/G1052/seed/ride.tabl.asabap | 12 + .../train/G1052/seed/seed_data.clas.abap | 37 + tasks_gen/train/G1052/spec.md | 40 + tasks_gen/train/G1052/task.json | 72 + .../G1055/faulty/m0_garden_rent.clas.abap | 94 + .../G1055/faulty/m1_garden_rent.clas.abap | 94 + .../G1055/faulty/m2_garden_rent.clas.abap | 94 + .../G1055/faulty/m3_garden_rent.clas.abap | 94 + tasks_gen/train/G1055/generation.json | 38 + .../G1055/hidden/t02_garden_hidden.clas.abap | 104 ++ tasks_gen/train/G1055/mutation.json | 68 + .../G1055/reference/garden_rent.clas.abap | 94 + .../reference/garden_rent.testclasses.abap | 71 + tasks_gen/train/G1055/spec.md | 61 + tasks_gen/train/G1055/task.json | 41 + tasks_gen/train/G1300/generation.json | 16 + .../train/G1300/hidden/t20_hidden.clas.abap | 108 ++ .../G1300/reference/i_cont_weight.ddls.asddls | 18 + .../train/G1300/reference/t20_test.clas.abap | 53 + tasks_gen/train/G1300/seed/cont.tabl.asabap | 12 + tasks_gen/train/G1300/seed/weigh.tabl.asabap | 10 + tasks_gen/train/G1300/spec.md | 1 + tasks_gen/train/G1300/task.json | 70 + tasks_gen/train/G1301/generation.json | 16 + .../G1301/hidden/harvest_hidden.clas.abap | 193 ++ .../reference/harvest_evaluator.clas.abap | 71 + .../harvest_evaluator.testclasses.abap | 84 + .../train/G1301/seed/if_harvest.intf.abap | 26 + tasks_gen/train/G1301/spec.md | 7 + tasks_gen/train/G1301/task.json | 55 + tasks_gen/train/G1302/generation.json | 16 + .../train/G1302/hidden/t14_hidden.clas.abap | 133 ++ .../G1302/reference/auc_premium.func.abap | 41 + .../train/G1302/reference/t14_test.clas.abap | 99 + .../train/G1302/seed/auc_sample.func.abap | 15 + tasks_gen/train/G1302/spec.md | 5 + tasks_gen/train/G1302/task.json | 86 + tasks_gen/train/G1303/generation.json | 16 + .../G1303/hidden/glasses_hidden.clas.abap | 100 ++ .../G1303/reference/glasses_pricer.clas.abap | 109 ++ .../reference/glasses_pricer.testclasses.abap | 83 + .../train/G1303/seed/glasses_pricer.clas.abap | 75 + .../G1303/seed/optic_quote_rpt.prog.abap | 12 + tasks_gen/train/G1303/spec.md | 9 + tasks_gen/train/G1303/task.json | 59 + tasks_gen/train/G1304/generation.json | 16 + .../hidden/repair_charge_hidden.clas.abap | 97 + .../reference/i_repair_charge.ddls.asddls | 15 + .../reference/repair_charge_test.clas.abap | 59 + .../G1304/seed/i_repair_charge.ddls.asddls | 14 + tasks_gen/train/G1304/seed/job.tabl.asabap | 14 + tasks_gen/train/G1304/seed/rate.tabl.asabap | 11 + .../train/G1304/seed/seed_repair.clas.abap | 34 + tasks_gen/train/G1304/spec.md | 1 + tasks_gen/train/G1304/task.json | 80 + tasks_gen/train/G1305/generation.json | 16 + .../train/G1305/hidden/ad_hidden.clas.abap | 125 ++ .../train/G1305/reference/ad_price.func.abap | 48 + .../train/G1305/reference/ad_test.clas.abap | 92 + tasks_gen/train/G1305/spec.md | 1 + tasks_gen/train/G1305/task.json | 78 + tasks_gen/train/G1306/generation.json | 16 + .../train/G1306/hidden/t21_hidden.clas.abap | 172 ++ .../G1306/reference/drone_checker.clas.abap | 49 + .../reference/drone_checker.testclasses.abap | 110 ++ .../train/G1306/seed/if_drone_check.intf.abap | 25 + tasks_gen/train/G1306/spec.md | 1 + tasks_gen/train/G1306/task.json | 53 + tasks_gen/train/G1307/generation.json | 16 + .../train/G1307/hidden/t16_hidden.clas.abap | 99 + .../G1307/reference/i_milk_total.ddls.asddls | 16 + .../train/G1307/reference/t16_test.clas.abap | 66 + tasks_gen/train/G1307/seed/farm.tabl.asabap | 11 + tasks_gen/train/G1307/seed/milk.tabl.asabap | 11 + tasks_gen/train/G1307/spec.md | 20 + tasks_gen/train/G1307/task.json | 67 + tasks_gen/train/G1308/generation.json | 16 + .../G1308/hidden/balloon_hidden.clas.abap | 169 ++ .../G1308/reference/balloon_checker.clas.abap | 52 + .../balloon_checker.testclasses.abap | 89 + .../reference/cx_balloon_cloud.clas.abap | 16 + .../G1308/reference/cx_balloon_crew.clas.abap | 16 + .../reference/cx_balloon_weight.clas.abap | 16 + .../G1308/reference/cx_balloon_wind.clas.abap | 16 + .../G1308/seed/cx_balloon_flight.clas.abap | 18 + tasks_gen/train/G1308/spec.md | 13 + tasks_gen/train/G1308/task.json | 92 + tasks_gen/train/G1309/generation.json | 16 + .../train/G1309/hidden/t33_hidden.clas.abap | 86 + .../G1309/reference/i_plant_grade.ddls.asddls | 21 + .../train/G1309/reference/t33_test.clas.abap | 43 + tasks_gen/train/G1309/seed/plant.tabl.asabap | 11 + tasks_gen/train/G1309/spec.md | 5 + tasks_gen/train/G1309/task.json | 61 + tasks_gen/train/G1310/generation.json | 16 + .../train/G1310/hidden/t02_hidden.clas.abap | 129 ++ .../G1310/reference/credit_checker.clas.abap | 59 + .../reference/credit_checker.testclasses.abap | 98 + .../G1310/seed/if_credit_check.intf.abap | 18 + tasks_gen/train/G1310/spec.md | 5 + tasks_gen/train/G1310/task.json | 53 + tasks_gen/train/G1311/generation.json | 16 + .../train/G1311/hidden/t31_hidden.clas.abap | 140 ++ .../G1311/reference/fish_auction.clas.abap | 96 + .../reference/fish_auction.testclasses.abap | 77 + tasks_gen/train/G1311/spec.md | 5 + tasks_gen/train/G1311/task.json | 45 + tasks_gen/train/G1312/generation.json | 16 + .../train/G1312/hidden/t31_hidden.clas.abap | 137 ++ .../reference/phone_repair_pricer.clas.abap | 105 ++ .../phone_repair_pricer.testclasses.abap | 84 + .../G1312/seed/phone_repair_pricer.clas.abap | 90 + tasks_gen/train/G1312/spec.md | 13 + tasks_gen/train/G1312/task.json | 51 + tasks_gen/train/G1313/generation.json | 16 + .../G1313/hidden/t_cinema_hidden.clas.abap | 76 + .../reference/i_film_revenue.ddls.asddls | 13 + .../G1313/reference/t_cinema_test.clas.abap | 46 + .../G1313/seed/i_film_revenue.ddls.asddls | 15 + tasks_gen/train/G1313/seed/screen.tabl.asabap | 13 + .../train/G1313/seed/seed_cinema.clas.abap | 28 + tasks_gen/train/G1313/seed/ticket.tabl.asabap | 11 + tasks_gen/train/G1313/spec.md | 1 + tasks_gen/train/G1313/task.json | 80 + tasks_gen/train/G1314/generation.json | 16 + .../train/G1314/hidden/t88_hidden.clas.abap | 129 ++ .../train/G1314/reference/guard_fee.func.abap | 45 + .../train/G1314/reference/t88_test.clas.abap | 68 + .../train/G1314/seed/guard_sample.func.abap | 14 + tasks_gen/train/G1314/spec.md | 5 + tasks_gen/train/G1314/task.json | 83 + tasks_gen/train/G1315/generation.json | 16 + .../train/G1315/hidden/t20_hidden.clas.abap | 138 ++ .../G1315/reference/log_merger.clas.abap | 84 + .../reference/log_merger.testclasses.abap | 71 + tasks_gen/train/G1315/spec.md | 7 + tasks_gen/train/G1315/task.json | 44 + tasks_gen/train/G1316/generation.json | 16 + .../hidden/frame_pricer_hidden.clas.abap | 171 ++ .../G1316/reference/frame_pricer.clas.abap | 189 ++ .../reference/frame_pricer.testclasses.abap | 87 + .../train/G1316/seed/frame_pricer.clas.abap | 127 ++ tasks_gen/train/G1316/spec.md | 1 + tasks_gen/train/G1316/task.json | 50 + tasks_gen/train/G1317/generation.json | 16 + .../train/G1317/hidden/t02_hidden.clas.abap | 169 ++ .../G1317/reference/towing_fee.clas.abap | 133 ++ .../reference/towing_fee.testclasses.abap | 74 + tasks_gen/train/G1317/spec.md | 18 + tasks_gen/train/G1317/task.json | 43 + tasks_gen/train/_logs/G1000.json | 21 + tasks_gen/train/_logs/G1001.json | 21 + tasks_gen/train/_logs/G1002.json | 26 + tasks_gen/train/_logs/G1003.json | 26 + tasks_gen/train/_logs/G1004.json | 26 + tasks_gen/train/_logs/G1005.json | 26 + tasks_gen/train/_logs/G1006.json | 62 + tasks_gen/train/_logs/G1007.json | 16 + tasks_gen/train/_logs/G1008.json | 35 + tasks_gen/train/_logs/G1009.json | 31 + tasks_gen/train/_logs/G1010.json | 30 + tasks_gen/train/_logs/G1011.json | 21 + tasks_gen/train/_logs/G1012.json | 31 + tasks_gen/train/_logs/G1013.json | 21 + tasks_gen/train/_logs/G1014.json | 54 + tasks_gen/train/_logs/G1015.json | 21 + tasks_gen/train/_logs/G1016.json | 37 + tasks_gen/train/_logs/G1017.json | 21 + tasks_gen/train/_logs/G1018.json | 54 + tasks_gen/train/_logs/G1019.json | 25 + tasks_gen/train/_logs/G1020.json | 31 + tasks_gen/train/_logs/G1021.json | 31 + tasks_gen/train/_logs/G1022.json | 21 + tasks_gen/train/_logs/G1023.json | 26 + tasks_gen/train/_logs/G1024.json | 26 + tasks_gen/train/_logs/G1025.json | 40 + tasks_gen/train/_logs/G1026.json | 30 + tasks_gen/train/_logs/G1027.json | 32 + tasks_gen/train/_logs/G1028.json | 52 + tasks_gen/train/_logs/G1029.json | 31 + tasks_gen/train/_logs/G1030.json | 38 + tasks_gen/train/_logs/G1031.json | 21 + tasks_gen/train/_logs/G1032.json | 30 + tasks_gen/train/_logs/G1033.json | 21 + tasks_gen/train/_logs/G1034.json | 21 + tasks_gen/train/_logs/G1035.json | 21 + tasks_gen/train/_logs/G1036.json | 26 + tasks_gen/train/_logs/G1037.json | 21 + tasks_gen/train/_logs/G1038.json | 26 + tasks_gen/train/_logs/G1039.json | 16 + tasks_gen/train/_logs/G1040.json | 21 + tasks_gen/train/_logs/G1041.json | 31 + tasks_gen/train/_logs/G1043.json | 21 + tasks_gen/train/_logs/G1046.json | 54 + tasks_gen/train/_logs/G1049.json | 30 + tasks_gen/train/_logs/G1052.json | 21 + tasks_gen/train/_logs/G1055.json | 40 + tasks_gen/train/_logs/G1300.json | 18 + tasks_gen/train/_logs/G1301.json | 18 + tasks_gen/train/_logs/G1302.json | 18 + tasks_gen/train/_logs/G1303.json | 18 + tasks_gen/train/_logs/G1304.json | 18 + tasks_gen/train/_logs/G1305.json | 18 + tasks_gen/train/_logs/G1306.json | 18 + tasks_gen/train/_logs/G1307.json | 18 + tasks_gen/train/_logs/G1308.json | 18 + tasks_gen/train/_logs/G1309.json | 18 + tasks_gen/train/_logs/G1310.json | 18 + tasks_gen/train/_logs/G1311.json | 18 + tasks_gen/train/_logs/G1312.json | 18 + tasks_gen/train/_logs/G1313.json | 18 + tasks_gen/train/_logs/G1314.json | 18 + tasks_gen/train/_logs/G1315.json | 18 + tasks_gen/train/_logs/G1316.json | 18 + tasks_gen/train/_logs/G1317.json | 18 + tasks_gen/train/plan.json | 1599 +++++++++++++++++ 781 files changed, 50211 insertions(+), 22 deletions(-) create mode 100644 harness/pipeline.py create mode 100644 tasks_gen/train/G1000/faulty/m0_i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/faulty/m1_i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/faulty/m2_i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/faulty/m3_i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/faulty/m4_i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/generation.json create mode 100644 tasks_gen/train/G1000/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1000/mutation.json create mode 100644 tasks_gen/train/G1000/reference/i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1000/reference/t20_test.clas.abap create mode 100644 tasks_gen/train/G1000/seed/cont.tabl.asabap create mode 100644 tasks_gen/train/G1000/seed/weigh.tabl.asabap create mode 100644 tasks_gen/train/G1000/spec.md create mode 100644 tasks_gen/train/G1000/task.json create mode 100644 tasks_gen/train/G1001/faulty/m0_harvest_evaluator.clas.abap create mode 100644 tasks_gen/train/G1001/faulty/m2_harvest_evaluator.clas.abap create mode 100644 tasks_gen/train/G1001/faulty/m3_harvest_evaluator.clas.abap create mode 100644 tasks_gen/train/G1001/generation.json create mode 100644 tasks_gen/train/G1001/hidden/harvest_hidden.clas.abap create mode 100644 tasks_gen/train/G1001/mutation.json create mode 100644 tasks_gen/train/G1001/reference/harvest_evaluator.clas.abap create mode 100644 tasks_gen/train/G1001/reference/harvest_evaluator.testclasses.abap create mode 100644 tasks_gen/train/G1001/seed/if_harvest.intf.abap create mode 100644 tasks_gen/train/G1001/spec.md create mode 100644 tasks_gen/train/G1001/task.json create mode 100644 tasks_gen/train/G1002/faulty/m0_glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/faulty/m1_glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/faulty/m2_glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/faulty/m3_glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/faulty/m4_glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/generation.json create mode 100644 tasks_gen/train/G1002/hidden/glasses_hidden.clas.abap create mode 100644 tasks_gen/train/G1002/mutation.json create mode 100644 tasks_gen/train/G1002/reference/glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/reference/glasses_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1002/seed/glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1002/seed/optic_quote_rpt.prog.abap create mode 100644 tasks_gen/train/G1002/spec.md create mode 100644 tasks_gen/train/G1002/task.json create mode 100644 tasks_gen/train/G1003/faulty/m0_drone_checker.clas.abap create mode 100644 tasks_gen/train/G1003/faulty/m1_drone_checker.clas.abap create mode 100644 tasks_gen/train/G1003/faulty/m2_drone_checker.clas.abap create mode 100644 tasks_gen/train/G1003/faulty/m3_drone_checker.clas.abap create mode 100644 tasks_gen/train/G1003/generation.json create mode 100644 tasks_gen/train/G1003/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1003/mutation.json create mode 100644 tasks_gen/train/G1003/reference/drone_checker.clas.abap create mode 100644 tasks_gen/train/G1003/reference/drone_checker.testclasses.abap create mode 100644 tasks_gen/train/G1003/seed/if_drone_check.intf.abap create mode 100644 tasks_gen/train/G1003/spec.md create mode 100644 tasks_gen/train/G1003/task.json create mode 100644 tasks_gen/train/G1004/faulty/m0_balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/faulty/m1_balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/faulty/m2_balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/faulty/m3_balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/faulty/m4_balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/generation.json create mode 100644 tasks_gen/train/G1004/hidden/balloon_hidden.clas.abap create mode 100644 tasks_gen/train/G1004/mutation.json create mode 100644 tasks_gen/train/G1004/reference/balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1004/reference/balloon_checker.testclasses.abap create mode 100644 tasks_gen/train/G1004/reference/cx_balloon_cloud.clas.abap create mode 100644 tasks_gen/train/G1004/reference/cx_balloon_crew.clas.abap create mode 100644 tasks_gen/train/G1004/reference/cx_balloon_weight.clas.abap create mode 100644 tasks_gen/train/G1004/reference/cx_balloon_wind.clas.abap create mode 100644 tasks_gen/train/G1004/seed/cx_balloon_flight.clas.abap create mode 100644 tasks_gen/train/G1004/spec.md create mode 100644 tasks_gen/train/G1004/task.json create mode 100644 tasks_gen/train/G1005/faulty/m0_credit_checker.clas.abap create mode 100644 tasks_gen/train/G1005/faulty/m2_credit_checker.clas.abap create mode 100644 tasks_gen/train/G1005/faulty/m3_credit_checker.clas.abap create mode 100644 tasks_gen/train/G1005/faulty/m4_credit_checker.clas.abap create mode 100644 tasks_gen/train/G1005/generation.json create mode 100644 tasks_gen/train/G1005/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1005/mutation.json create mode 100644 tasks_gen/train/G1005/reference/credit_checker.clas.abap create mode 100644 tasks_gen/train/G1005/reference/credit_checker.testclasses.abap create mode 100644 tasks_gen/train/G1005/seed/if_credit_check.intf.abap create mode 100644 tasks_gen/train/G1005/spec.md create mode 100644 tasks_gen/train/G1005/task.json create mode 100644 tasks_gen/train/G1006/faulty/m0_auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/faulty/m1_auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/faulty/m2_auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/faulty/m3_auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/faulty/m4_auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/generation.json create mode 100644 tasks_gen/train/G1006/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1006/mutation.json create mode 100644 tasks_gen/train/G1006/reference/auc_premium.func.abap create mode 100644 tasks_gen/train/G1006/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1006/seed/auc_sample.func.abap create mode 100644 tasks_gen/train/G1006/spec.md create mode 100644 tasks_gen/train/G1006/task.json create mode 100644 tasks_gen/train/G1007/generation.json create mode 100644 tasks_gen/train/G1007/reference/jump_pricer.clas.abap create mode 100644 tasks_gen/train/G1007/reference/jump_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1007/spec.md create mode 100644 tasks_gen/train/G1007/task.json create mode 100644 tasks_gen/train/G1008/generation.json create mode 100644 tasks_gen/train/G1008/hidden/turb_hidden.clas.abap create mode 100644 tasks_gen/train/G1008/mutation.json create mode 100644 tasks_gen/train/G1008/reference/i_turbine.ddls.asddls create mode 100644 tasks_gen/train/G1008/reference/turb_test.clas.abap create mode 100644 tasks_gen/train/G1008/seed/turbine.tabl.asabap create mode 100644 tasks_gen/train/G1008/seed/wpark.tabl.asabap create mode 100644 tasks_gen/train/G1008/spec.md create mode 100644 tasks_gen/train/G1008/task.json create mode 100644 tasks_gen/train/G1009/faulty/m0_fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/faulty/m1_fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/faulty/m2_fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/faulty/m3_fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/faulty/m4_fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/generation.json create mode 100644 tasks_gen/train/G1009/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1009/mutation.json create mode 100644 tasks_gen/train/G1009/reference/fish_auction.clas.abap create mode 100644 tasks_gen/train/G1009/reference/fish_auction.testclasses.abap create mode 100644 tasks_gen/train/G1009/spec.md create mode 100644 tasks_gen/train/G1009/task.json create mode 100644 tasks_gen/train/G1010/faulty/m0_phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/faulty/m1_phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/faulty/m2_phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/faulty/m3_phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/faulty/m4_phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/generation.json create mode 100644 tasks_gen/train/G1010/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1010/mutation.json create mode 100644 tasks_gen/train/G1010/reference/phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/reference/phone_repair_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1010/seed/phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1010/spec.md create mode 100644 tasks_gen/train/G1010/task.json create mode 100644 tasks_gen/train/G1011/faulty/m1_i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/faulty/m2_i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/faulty/m3_i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/faulty/m4_i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/generation.json create mode 100644 tasks_gen/train/G1011/hidden/repair_charge_hidden.clas.abap create mode 100644 tasks_gen/train/G1011/mutation.json create mode 100644 tasks_gen/train/G1011/reference/i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/reference/repair_charge_test.clas.abap create mode 100644 tasks_gen/train/G1011/seed/i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1011/seed/job.tabl.asabap create mode 100644 tasks_gen/train/G1011/seed/rate.tabl.asabap create mode 100644 tasks_gen/train/G1011/seed/seed_repair.clas.abap create mode 100644 tasks_gen/train/G1011/spec.md create mode 100644 tasks_gen/train/G1011/task.json create mode 100644 tasks_gen/train/G1012/faulty/m0_i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/faulty/m1_i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/faulty/m2_i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/faulty/m3_i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/faulty/m4_i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/generation.json create mode 100644 tasks_gen/train/G1012/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1012/mutation.json create mode 100644 tasks_gen/train/G1012/reference/i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1012/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1012/seed/farm.tabl.asabap create mode 100644 tasks_gen/train/G1012/seed/milk.tabl.asabap create mode 100644 tasks_gen/train/G1012/spec.md create mode 100644 tasks_gen/train/G1012/task.json create mode 100644 tasks_gen/train/G1013/faulty/m0_log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/faulty/m1_log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/faulty/m2_log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/faulty/m3_log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/faulty/m4_log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/generation.json create mode 100644 tasks_gen/train/G1013/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1013/mutation.json create mode 100644 tasks_gen/train/G1013/reference/log_merger.clas.abap create mode 100644 tasks_gen/train/G1013/reference/log_merger.testclasses.abap create mode 100644 tasks_gen/train/G1013/spec.md create mode 100644 tasks_gen/train/G1013/task.json create mode 100644 tasks_gen/train/G1014/faulty/m0_frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/faulty/m1_frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/faulty/m2_frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/faulty/m3_frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/faulty/m4_frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/generation.json create mode 100644 tasks_gen/train/G1014/hidden/frame_pricer_hidden.clas.abap create mode 100644 tasks_gen/train/G1014/mutation.json create mode 100644 tasks_gen/train/G1014/reference/frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/reference/frame_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1014/seed/frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1014/spec.md create mode 100644 tasks_gen/train/G1014/task.json create mode 100644 tasks_gen/train/G1015/faulty/m0_towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/faulty/m1_towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/faulty/m2_towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/faulty/m3_towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/faulty/m4_towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/generation.json create mode 100644 tasks_gen/train/G1015/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1015/mutation.json create mode 100644 tasks_gen/train/G1015/reference/towing_fee.clas.abap create mode 100644 tasks_gen/train/G1015/reference/towing_fee.testclasses.abap create mode 100644 tasks_gen/train/G1015/spec.md create mode 100644 tasks_gen/train/G1015/task.json create mode 100644 tasks_gen/train/G1016/generation.json create mode 100644 tasks_gen/train/G1016/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1016/reference/cx_return_error.clas.abap create mode 100644 tasks_gen/train/G1016/reference/return_checker.clas.abap create mode 100644 tasks_gen/train/G1016/reference/return_checker.testclasses.abap create mode 100644 tasks_gen/train/G1016/seed/seed_vehicle.clas.abap create mode 100644 tasks_gen/train/G1016/seed/vehicle.tabl.abap create mode 100644 tasks_gen/train/G1016/spec.md create mode 100644 tasks_gen/train/G1016/task.json create mode 100644 tasks_gen/train/G1017/faulty/m0_inspection_checker.clas.abap create mode 100644 tasks_gen/train/G1017/faulty/m2_inspection_checker.clas.abap create mode 100644 tasks_gen/train/G1017/faulty/m3_inspection_checker.clas.abap create mode 100644 tasks_gen/train/G1017/generation.json create mode 100644 tasks_gen/train/G1017/hidden/t_f01_hidden.clas.abap create mode 100644 tasks_gen/train/G1017/mutation.json create mode 100644 tasks_gen/train/G1017/reference/inspection_checker.clas.abap create mode 100644 tasks_gen/train/G1017/reference/inspection_checker.testclasses.abap create mode 100644 tasks_gen/train/G1017/seed/if_inspection_check.intf.abap create mode 100644 tasks_gen/train/G1017/spec.md create mode 100644 tasks_gen/train/G1017/task.json create mode 100644 tasks_gen/train/G1018/faulty/m0_ad_price.func.abap create mode 100644 tasks_gen/train/G1018/faulty/m1_ad_price.func.abap create mode 100644 tasks_gen/train/G1018/faulty/m2_ad_price.func.abap create mode 100644 tasks_gen/train/G1018/faulty/m3_ad_price.func.abap create mode 100644 tasks_gen/train/G1018/faulty/m4_ad_price.func.abap create mode 100644 tasks_gen/train/G1018/generation.json create mode 100644 tasks_gen/train/G1018/hidden/ad_hidden.clas.abap create mode 100644 tasks_gen/train/G1018/mutation.json create mode 100644 tasks_gen/train/G1018/reference/ad_price.func.abap create mode 100644 tasks_gen/train/G1018/reference/ad_test.clas.abap create mode 100644 tasks_gen/train/G1018/spec.md create mode 100644 tasks_gen/train/G1018/task.json create mode 100644 tasks_gen/train/G1019/generation.json create mode 100644 tasks_gen/train/G1019/reference/bowling_fee.clas.abap create mode 100644 tasks_gen/train/G1019/reference/bowling_fee.testclasses.abap create mode 100644 tasks_gen/train/G1019/seed/if_bowling_fee.intf.abap create mode 100644 tasks_gen/train/G1019/spec.md create mode 100644 tasks_gen/train/G1019/task.json create mode 100644 tasks_gen/train/G1020/faulty/m0_i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1020/faulty/m1_i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1020/faulty/m3_i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1020/faulty/m4_i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1020/generation.json create mode 100644 tasks_gen/train/G1020/hidden/t33_hidden.clas.abap create mode 100644 tasks_gen/train/G1020/mutation.json create mode 100644 tasks_gen/train/G1020/reference/i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1020/reference/t33_test.clas.abap create mode 100644 tasks_gen/train/G1020/seed/plant.tabl.asabap create mode 100644 tasks_gen/train/G1020/spec.md create mode 100644 tasks_gen/train/G1020/task.json create mode 100644 tasks_gen/train/G1021/generation.json create mode 100644 tasks_gen/train/G1021/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/train/G1021/reference/roast_merger.clas.abap create mode 100644 tasks_gen/train/G1021/reference/roast_merger.testclasses.abap create mode 100644 tasks_gen/train/G1021/spec.md create mode 100644 tasks_gen/train/G1021/task.json create mode 100644 tasks_gen/train/G1022/faulty/m0_sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/faulty/m1_sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/faulty/m2_sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/faulty/m3_sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/faulty/m4_sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/generation.json create mode 100644 tasks_gen/train/G1022/hidden/sweep_hidden.clas.abap create mode 100644 tasks_gen/train/G1022/mutation.json create mode 100644 tasks_gen/train/G1022/reference/sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/reference/sweep_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1022/seed/sweep_pricer.clas.abap create mode 100644 tasks_gen/train/G1022/spec.md create mode 100644 tasks_gen/train/G1022/task.json create mode 100644 tasks_gen/train/G1023/faulty/m0_rideshare_split.clas.abap create mode 100644 tasks_gen/train/G1023/faulty/m1_rideshare_split.clas.abap create mode 100644 tasks_gen/train/G1023/faulty/m3_rideshare_split.clas.abap create mode 100644 tasks_gen/train/G1023/faulty/m4_rideshare_split.clas.abap create mode 100644 tasks_gen/train/G1023/generation.json create mode 100644 tasks_gen/train/G1023/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1023/mutation.json create mode 100644 tasks_gen/train/G1023/reference/rideshare_split.clas.abap create mode 100644 tasks_gen/train/G1023/reference/rideshare_split.testclasses.abap create mode 100644 tasks_gen/train/G1023/spec.md create mode 100644 tasks_gen/train/G1023/task.json create mode 100644 tasks_gen/train/G1024/faulty/m0_holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/faulty/m1_holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/faulty/m2_holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/faulty/m3_holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/faulty/m4_holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/generation.json create mode 100644 tasks_gen/train/G1024/hidden/holiday_hidden.clas.abap create mode 100644 tasks_gen/train/G1024/mutation.json create mode 100644 tasks_gen/train/G1024/reference/cx_bad_departure.clas.abap create mode 100644 tasks_gen/train/G1024/reference/cx_bad_nights.clas.abap create mode 100644 tasks_gen/train/G1024/reference/cx_bad_travellers.clas.abap create mode 100644 tasks_gen/train/G1024/reference/cx_unknown_dest.clas.abap create mode 100644 tasks_gen/train/G1024/reference/holiday_checker.clas.abap create mode 100644 tasks_gen/train/G1024/reference/holiday_checker.testclasses.abap create mode 100644 tasks_gen/train/G1024/seed/cx_holiday_error.clas.abap create mode 100644 tasks_gen/train/G1024/spec.md create mode 100644 tasks_gen/train/G1024/task.json create mode 100644 tasks_gen/train/G1025/faulty/m0_crane_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1025/faulty/m1_crane_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1025/faulty/m2_crane_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1025/faulty/m4_crane_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1025/generation.json create mode 100644 tasks_gen/train/G1025/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1025/mutation.json create mode 100644 tasks_gen/train/G1025/reference/crane_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1025/reference/crane_hire_calc.testclasses.abap create mode 100644 tasks_gen/train/G1025/seed/if_crane_hire.intf.abap create mode 100644 tasks_gen/train/G1025/spec.md create mode 100644 tasks_gen/train/G1025/task.json create mode 100644 tasks_gen/train/G1026/faulty/m0_guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/faulty/m1_guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/faulty/m2_guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/faulty/m3_guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/faulty/m4_guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/generation.json create mode 100644 tasks_gen/train/G1026/hidden/t88_hidden.clas.abap create mode 100644 tasks_gen/train/G1026/mutation.json create mode 100644 tasks_gen/train/G1026/reference/guard_fee.func.abap create mode 100644 tasks_gen/train/G1026/reference/t88_test.clas.abap create mode 100644 tasks_gen/train/G1026/seed/guard_sample.func.abap create mode 100644 tasks_gen/train/G1026/spec.md create mode 100644 tasks_gen/train/G1026/task.json create mode 100644 tasks_gen/train/G1027/generation.json create mode 100644 tasks_gen/train/G1027/reference/scaffold_fee.clas.abap create mode 100644 tasks_gen/train/G1027/reference/scaffold_fee.testclasses.abap create mode 100644 tasks_gen/train/G1027/seed/if_hire_fee.intf.abap create mode 100644 tasks_gen/train/G1027/spec.md create mode 100644 tasks_gen/train/G1027/task.json create mode 100644 tasks_gen/train/G1028/generation.json create mode 100644 tasks_gen/train/G1028/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1028/mutation.json create mode 100644 tasks_gen/train/G1028/reference/i_dept.ddls.asddls create mode 100644 tasks_gen/train/G1028/reference/t20_test.clas.abap create mode 100644 tasks_gen/train/G1028/seed/dept.tabl.asabap create mode 100644 tasks_gen/train/G1028/seed/seed_dept.clas.abap create mode 100644 tasks_gen/train/G1028/spec.md create mode 100644 tasks_gen/train/G1028/task.json create mode 100644 tasks_gen/train/G1029/faulty/m0_licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/faulty/m1_licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/faulty/m2_licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/faulty/m3_licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/faulty/m4_licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/generation.json create mode 100644 tasks_gen/train/G1029/hidden/t_lic_hidden.clas.abap create mode 100644 tasks_gen/train/G1029/mutation.json create mode 100644 tasks_gen/train/G1029/reference/licence_merger.clas.abap create mode 100644 tasks_gen/train/G1029/reference/licence_merger.testclasses.abap create mode 100644 tasks_gen/train/G1029/seed/if_licence_merge.intf.abap create mode 100644 tasks_gen/train/G1029/spec.md create mode 100644 tasks_gen/train/G1029/task.json create mode 100644 tasks_gen/train/G1030/faulty/m0_skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/faulty/m1_skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/faulty/m2_skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/faulty/m4_skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/generation.json create mode 100644 tasks_gen/train/G1030/hidden/t61_hidden.clas.abap create mode 100644 tasks_gen/train/G1030/mutation.json create mode 100644 tasks_gen/train/G1030/reference/skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/reference/skip_hire_calc.testclasses.abap create mode 100644 tasks_gen/train/G1030/seed/skip_hire_calc.clas.abap create mode 100644 tasks_gen/train/G1030/spec.md create mode 100644 tasks_gen/train/G1030/task.json create mode 100644 tasks_gen/train/G1031/faulty/m0_i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/faulty/m1_i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/faulty/m2_i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/faulty/m3_i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/faulty/m4_i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/generation.json create mode 100644 tasks_gen/train/G1031/hidden/t_cinema_hidden.clas.abap create mode 100644 tasks_gen/train/G1031/mutation.json create mode 100644 tasks_gen/train/G1031/reference/i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/reference/t_cinema_test.clas.abap create mode 100644 tasks_gen/train/G1031/seed/i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1031/seed/screen.tabl.asabap create mode 100644 tasks_gen/train/G1031/seed/seed_cinema.clas.abap create mode 100644 tasks_gen/train/G1031/seed/ticket.tabl.asabap create mode 100644 tasks_gen/train/G1031/spec.md create mode 100644 tasks_gen/train/G1031/task.json create mode 100644 tasks_gen/train/G1032/faulty/m1_i_tutor_load.ddls.asddls create mode 100644 tasks_gen/train/G1032/faulty/m2_i_tutor_load.ddls.asddls create mode 100644 tasks_gen/train/G1032/faulty/m3_i_tutor_load.ddls.asddls create mode 100644 tasks_gen/train/G1032/faulty/m4_i_tutor_load.ddls.asddls create mode 100644 tasks_gen/train/G1032/generation.json create mode 100644 tasks_gen/train/G1032/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1032/mutation.json create mode 100644 tasks_gen/train/G1032/reference/i_tutor_load.ddls.asddls create mode 100644 tasks_gen/train/G1032/reference/t31_test.clas.abap create mode 100644 tasks_gen/train/G1032/seed/lesson.tabl.asabap create mode 100644 tasks_gen/train/G1032/seed/tutor.tabl.asabap create mode 100644 tasks_gen/train/G1032/spec.md create mode 100644 tasks_gen/train/G1032/task.json create mode 100644 tasks_gen/train/G1033/faulty/m0_oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/faulty/m1_oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/faulty/m2_oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/faulty/m3_oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/faulty/m4_oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/generation.json create mode 100644 tasks_gen/train/G1033/hidden/t_oil_hidden.clas.abap create mode 100644 tasks_gen/train/G1033/mutation.json create mode 100644 tasks_gen/train/G1033/reference/oil_settlement.clas.abap create mode 100644 tasks_gen/train/G1033/reference/oil_settlement.testclasses.abap create mode 100644 tasks_gen/train/G1033/spec.md create mode 100644 tasks_gen/train/G1033/task.json create mode 100644 tasks_gen/train/G1034/faulty/m0_storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/faulty/m1_storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/faulty/m2_storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/faulty/m3_storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/faulty/m4_storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/generation.json create mode 100644 tasks_gen/train/G1034/hidden/t_stor_hidden.clas.abap create mode 100644 tasks_gen/train/G1034/mutation.json create mode 100644 tasks_gen/train/G1034/reference/storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/reference/storage_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1034/seed/storage_pricer.clas.abap create mode 100644 tasks_gen/train/G1034/spec.md create mode 100644 tasks_gen/train/G1034/task.json create mode 100644 tasks_gen/train/G1035/faulty/m0_photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/faulty/m1_photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/faulty/m2_photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/faulty/m3_photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/faulty/m4_photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/generation.json create mode 100644 tasks_gen/train/G1035/hidden/t120_hidden.clas.abap create mode 100644 tasks_gen/train/G1035/mutation.json create mode 100644 tasks_gen/train/G1035/reference/photo_pricer.clas.abap create mode 100644 tasks_gen/train/G1035/reference/photo_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1035/spec.md create mode 100644 tasks_gen/train/G1035/task.json create mode 100644 tasks_gen/train/G1036/faulty/m0_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/faulty/m1_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/faulty/m2_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/faulty/m3_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/faulty/m4_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/generation.json create mode 100644 tasks_gen/train/G1036/hidden/ride_hidden.clas.abap create mode 100644 tasks_gen/train/G1036/mutation.json create mode 100644 tasks_gen/train/G1036/reference/ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/reference/ride_booking.testclasses.abap create mode 100644 tasks_gen/train/G1036/seed/cx_ride_booking.clas.abap create mode 100644 tasks_gen/train/G1036/seed/cx_ride_input.clas.abap create mode 100644 tasks_gen/train/G1036/seed/cx_ride_limit.clas.abap create mode 100644 tasks_gen/train/G1036/seed/if_ride_booking.intf.abap create mode 100644 tasks_gen/train/G1036/spec.md create mode 100644 tasks_gen/train/G1036/task.json create mode 100644 tasks_gen/train/G1037/faulty/m0_piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/faulty/m1_piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/faulty/m2_piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/faulty/m3_piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/faulty/m4_piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/generation.json create mode 100644 tasks_gen/train/G1037/hidden/t42_hidden.clas.abap create mode 100644 tasks_gen/train/G1037/mutation.json create mode 100644 tasks_gen/train/G1037/reference/piano_pricer.clas.abap create mode 100644 tasks_gen/train/G1037/reference/piano_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1037/seed/tune_rates.clas.abap create mode 100644 tasks_gen/train/G1037/spec.md create mode 100644 tasks_gen/train/G1037/task.json create mode 100644 tasks_gen/train/G1038/faulty/m0_mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/faulty/m1_mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/faulty/m2_mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/faulty/m3_mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/faulty/m4_mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/generation.json create mode 100644 tasks_gen/train/G1038/hidden/t_mkt_hidden.clas.abap create mode 100644 tasks_gen/train/G1038/mutation.json create mode 100644 tasks_gen/train/G1038/reference/mkt_fee_test.clas.abap create mode 100644 tasks_gen/train/G1038/reference/mkt_stall_fee.func.abap create mode 100644 tasks_gen/train/G1038/seed/mkt_sample.func.abap create mode 100644 tasks_gen/train/G1038/spec.md create mode 100644 tasks_gen/train/G1038/task.json create mode 100644 tasks_gen/train/G1039/generation.json create mode 100644 tasks_gen/train/G1039/reference/tour_summary.clas.abap create mode 100644 tasks_gen/train/G1039/reference/tour_summary.testclasses.abap create mode 100644 tasks_gen/train/G1039/seed/if_tour_summary.intf.abap create mode 100644 tasks_gen/train/G1039/spec.md create mode 100644 tasks_gen/train/G1039/task.json create mode 100644 tasks_gen/train/G1040/faulty/m0_i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/faulty/m1_i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/faulty/m2_i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/faulty/m3_i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/faulty/m4_i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/generation.json create mode 100644 tasks_gen/train/G1040/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1040/mutation.json create mode 100644 tasks_gen/train/G1040/reference/i_roll_grade.ddls.asddls create mode 100644 tasks_gen/train/G1040/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1040/seed/fabric.tabl.asabap create mode 100644 tasks_gen/train/G1040/seed/roll.tabl.asabap create mode 100644 tasks_gen/train/G1040/spec.md create mode 100644 tasks_gen/train/G1040/task.json create mode 100644 tasks_gen/train/G1041/generation.json create mode 100644 tasks_gen/train/G1041/hidden/feed_hidden.clas.abap create mode 100644 tasks_gen/train/G1041/reference/feed_stock.clas.abap create mode 100644 tasks_gen/train/G1041/reference/feed_stock.testclasses.abap create mode 100644 tasks_gen/train/G1041/seed/if_feed_stock.intf.abap create mode 100644 tasks_gen/train/G1041/spec.md create mode 100644 tasks_gen/train/G1041/task.json create mode 100644 tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap create mode 100644 tasks_gen/train/G1042/reference/snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/reference/snow_fee.testclasses.abap create mode 100644 tasks_gen/train/G1042/seed/snow_fee.clas.abap create mode 100644 tasks_gen/train/G1042/spec.md create mode 100644 tasks_gen/train/G1042/task.json create mode 100644 tasks_gen/train/G1043/faulty/m0_med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/faulty/m1_med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/faulty/m2_med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/faulty/m3_med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/faulty/m4_med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/generation.json create mode 100644 tasks_gen/train/G1043/hidden/t40_hidden.clas.abap create mode 100644 tasks_gen/train/G1043/mutation.json create mode 100644 tasks_gen/train/G1043/reference/med_copay.clas.abap create mode 100644 tasks_gen/train/G1043/reference/med_copay.testclasses.abap create mode 100644 tasks_gen/train/G1043/seed/if_med_copay.intf.abap create mode 100644 tasks_gen/train/G1043/spec.md create mode 100644 tasks_gen/train/G1043/task.json create mode 100644 tasks_gen/train/G1044/faulty/m0_escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/faulty/m1_escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1044/reference/escape_checker.clas.abap create mode 100644 tasks_gen/train/G1044/reference/escape_checker.testclasses.abap create mode 100644 tasks_gen/train/G1044/seed/cx_escape_age.clas.abap create mode 100644 tasks_gen/train/G1044/seed/cx_escape_error.clas.abap create mode 100644 tasks_gen/train/G1044/seed/cx_escape_players.clas.abap create mode 100644 tasks_gen/train/G1044/seed/cx_escape_room.clas.abap create mode 100644 tasks_gen/train/G1044/seed/cx_escape_time.clas.abap create mode 100644 tasks_gen/train/G1044/spec.md create mode 100644 tasks_gen/train/G1044/task.json create mode 100644 tasks_gen/train/G1046/faulty/m0_tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/faulty/m1_tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/faulty/m2_tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/faulty/m3_tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/faulty/m4_tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/generation.json create mode 100644 tasks_gen/train/G1046/hidden/tree_hidden.clas.abap create mode 100644 tasks_gen/train/G1046/mutation.json create mode 100644 tasks_gen/train/G1046/reference/tree_fee.func.abap create mode 100644 tasks_gen/train/G1046/reference/tree_test.clas.abap create mode 100644 tasks_gen/train/G1046/spec.md create mode 100644 tasks_gen/train/G1046/task.json create mode 100644 tasks_gen/train/G1049/faulty/m0_quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/faulty/m1_quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/faulty/m2_quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/faulty/m3_quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/faulty/m4_quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/generation.json create mode 100644 tasks_gen/train/G1049/hidden/quote_merger_hidden.clas.abap create mode 100644 tasks_gen/train/G1049/mutation.json create mode 100644 tasks_gen/train/G1049/reference/quote_merger.clas.abap create mode 100644 tasks_gen/train/G1049/reference/quote_merger.testclasses.abap create mode 100644 tasks_gen/train/G1049/spec.md create mode 100644 tasks_gen/train/G1049/task.json create mode 100644 tasks_gen/train/G1052/faulty/m1_i_bike_use.ddls.asddls create mode 100644 tasks_gen/train/G1052/faulty/m2_i_bike_use.ddls.asddls create mode 100644 tasks_gen/train/G1052/faulty/m3_i_bike_use.ddls.asddls create mode 100644 tasks_gen/train/G1052/faulty/m4_i_bike_use.ddls.asddls create mode 100644 tasks_gen/train/G1052/generation.json create mode 100644 tasks_gen/train/G1052/hidden/t_bike_hid.clas.abap create mode 100644 tasks_gen/train/G1052/mutation.json create mode 100644 tasks_gen/train/G1052/reference/i_bike_use.ddls.asddls create mode 100644 tasks_gen/train/G1052/reference/t_bike_test.clas.abap create mode 100644 tasks_gen/train/G1052/seed/bike.tabl.asabap create mode 100644 tasks_gen/train/G1052/seed/ride.tabl.asabap create mode 100644 tasks_gen/train/G1052/seed/seed_data.clas.abap create mode 100644 tasks_gen/train/G1052/spec.md create mode 100644 tasks_gen/train/G1052/task.json create mode 100644 tasks_gen/train/G1055/faulty/m0_garden_rent.clas.abap create mode 100644 tasks_gen/train/G1055/faulty/m1_garden_rent.clas.abap create mode 100644 tasks_gen/train/G1055/faulty/m2_garden_rent.clas.abap create mode 100644 tasks_gen/train/G1055/faulty/m3_garden_rent.clas.abap create mode 100644 tasks_gen/train/G1055/generation.json create mode 100644 tasks_gen/train/G1055/hidden/t02_garden_hidden.clas.abap create mode 100644 tasks_gen/train/G1055/mutation.json create mode 100644 tasks_gen/train/G1055/reference/garden_rent.clas.abap create mode 100644 tasks_gen/train/G1055/reference/garden_rent.testclasses.abap create mode 100644 tasks_gen/train/G1055/spec.md create mode 100644 tasks_gen/train/G1055/task.json create mode 100644 tasks_gen/train/G1300/generation.json create mode 100644 tasks_gen/train/G1300/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1300/reference/i_cont_weight.ddls.asddls create mode 100644 tasks_gen/train/G1300/reference/t20_test.clas.abap create mode 100644 tasks_gen/train/G1300/seed/cont.tabl.asabap create mode 100644 tasks_gen/train/G1300/seed/weigh.tabl.asabap create mode 100644 tasks_gen/train/G1300/spec.md create mode 100644 tasks_gen/train/G1300/task.json create mode 100644 tasks_gen/train/G1301/generation.json create mode 100644 tasks_gen/train/G1301/hidden/harvest_hidden.clas.abap create mode 100644 tasks_gen/train/G1301/reference/harvest_evaluator.clas.abap create mode 100644 tasks_gen/train/G1301/reference/harvest_evaluator.testclasses.abap create mode 100644 tasks_gen/train/G1301/seed/if_harvest.intf.abap create mode 100644 tasks_gen/train/G1301/spec.md create mode 100644 tasks_gen/train/G1301/task.json create mode 100644 tasks_gen/train/G1302/generation.json create mode 100644 tasks_gen/train/G1302/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/train/G1302/reference/auc_premium.func.abap create mode 100644 tasks_gen/train/G1302/reference/t14_test.clas.abap create mode 100644 tasks_gen/train/G1302/seed/auc_sample.func.abap create mode 100644 tasks_gen/train/G1302/spec.md create mode 100644 tasks_gen/train/G1302/task.json create mode 100644 tasks_gen/train/G1303/generation.json create mode 100644 tasks_gen/train/G1303/hidden/glasses_hidden.clas.abap create mode 100644 tasks_gen/train/G1303/reference/glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1303/reference/glasses_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1303/seed/glasses_pricer.clas.abap create mode 100644 tasks_gen/train/G1303/seed/optic_quote_rpt.prog.abap create mode 100644 tasks_gen/train/G1303/spec.md create mode 100644 tasks_gen/train/G1303/task.json create mode 100644 tasks_gen/train/G1304/generation.json create mode 100644 tasks_gen/train/G1304/hidden/repair_charge_hidden.clas.abap create mode 100644 tasks_gen/train/G1304/reference/i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1304/reference/repair_charge_test.clas.abap create mode 100644 tasks_gen/train/G1304/seed/i_repair_charge.ddls.asddls create mode 100644 tasks_gen/train/G1304/seed/job.tabl.asabap create mode 100644 tasks_gen/train/G1304/seed/rate.tabl.asabap create mode 100644 tasks_gen/train/G1304/seed/seed_repair.clas.abap create mode 100644 tasks_gen/train/G1304/spec.md create mode 100644 tasks_gen/train/G1304/task.json create mode 100644 tasks_gen/train/G1305/generation.json create mode 100644 tasks_gen/train/G1305/hidden/ad_hidden.clas.abap create mode 100644 tasks_gen/train/G1305/reference/ad_price.func.abap create mode 100644 tasks_gen/train/G1305/reference/ad_test.clas.abap create mode 100644 tasks_gen/train/G1305/spec.md create mode 100644 tasks_gen/train/G1305/task.json create mode 100644 tasks_gen/train/G1306/generation.json create mode 100644 tasks_gen/train/G1306/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/train/G1306/reference/drone_checker.clas.abap create mode 100644 tasks_gen/train/G1306/reference/drone_checker.testclasses.abap create mode 100644 tasks_gen/train/G1306/seed/if_drone_check.intf.abap create mode 100644 tasks_gen/train/G1306/spec.md create mode 100644 tasks_gen/train/G1306/task.json create mode 100644 tasks_gen/train/G1307/generation.json create mode 100644 tasks_gen/train/G1307/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/train/G1307/reference/i_milk_total.ddls.asddls create mode 100644 tasks_gen/train/G1307/reference/t16_test.clas.abap create mode 100644 tasks_gen/train/G1307/seed/farm.tabl.asabap create mode 100644 tasks_gen/train/G1307/seed/milk.tabl.asabap create mode 100644 tasks_gen/train/G1307/spec.md create mode 100644 tasks_gen/train/G1307/task.json create mode 100644 tasks_gen/train/G1308/generation.json create mode 100644 tasks_gen/train/G1308/hidden/balloon_hidden.clas.abap create mode 100644 tasks_gen/train/G1308/reference/balloon_checker.clas.abap create mode 100644 tasks_gen/train/G1308/reference/balloon_checker.testclasses.abap create mode 100644 tasks_gen/train/G1308/reference/cx_balloon_cloud.clas.abap create mode 100644 tasks_gen/train/G1308/reference/cx_balloon_crew.clas.abap create mode 100644 tasks_gen/train/G1308/reference/cx_balloon_weight.clas.abap create mode 100644 tasks_gen/train/G1308/reference/cx_balloon_wind.clas.abap create mode 100644 tasks_gen/train/G1308/seed/cx_balloon_flight.clas.abap create mode 100644 tasks_gen/train/G1308/spec.md create mode 100644 tasks_gen/train/G1308/task.json create mode 100644 tasks_gen/train/G1309/generation.json create mode 100644 tasks_gen/train/G1309/hidden/t33_hidden.clas.abap create mode 100644 tasks_gen/train/G1309/reference/i_plant_grade.ddls.asddls create mode 100644 tasks_gen/train/G1309/reference/t33_test.clas.abap create mode 100644 tasks_gen/train/G1309/seed/plant.tabl.asabap create mode 100644 tasks_gen/train/G1309/spec.md create mode 100644 tasks_gen/train/G1309/task.json create mode 100644 tasks_gen/train/G1310/generation.json create mode 100644 tasks_gen/train/G1310/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1310/reference/credit_checker.clas.abap create mode 100644 tasks_gen/train/G1310/reference/credit_checker.testclasses.abap create mode 100644 tasks_gen/train/G1310/seed/if_credit_check.intf.abap create mode 100644 tasks_gen/train/G1310/spec.md create mode 100644 tasks_gen/train/G1310/task.json create mode 100644 tasks_gen/train/G1311/generation.json create mode 100644 tasks_gen/train/G1311/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1311/reference/fish_auction.clas.abap create mode 100644 tasks_gen/train/G1311/reference/fish_auction.testclasses.abap create mode 100644 tasks_gen/train/G1311/spec.md create mode 100644 tasks_gen/train/G1311/task.json create mode 100644 tasks_gen/train/G1312/generation.json create mode 100644 tasks_gen/train/G1312/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/train/G1312/reference/phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1312/reference/phone_repair_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1312/seed/phone_repair_pricer.clas.abap create mode 100644 tasks_gen/train/G1312/spec.md create mode 100644 tasks_gen/train/G1312/task.json create mode 100644 tasks_gen/train/G1313/generation.json create mode 100644 tasks_gen/train/G1313/hidden/t_cinema_hidden.clas.abap create mode 100644 tasks_gen/train/G1313/reference/i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1313/reference/t_cinema_test.clas.abap create mode 100644 tasks_gen/train/G1313/seed/i_film_revenue.ddls.asddls create mode 100644 tasks_gen/train/G1313/seed/screen.tabl.asabap create mode 100644 tasks_gen/train/G1313/seed/seed_cinema.clas.abap create mode 100644 tasks_gen/train/G1313/seed/ticket.tabl.asabap create mode 100644 tasks_gen/train/G1313/spec.md create mode 100644 tasks_gen/train/G1313/task.json create mode 100644 tasks_gen/train/G1314/generation.json create mode 100644 tasks_gen/train/G1314/hidden/t88_hidden.clas.abap create mode 100644 tasks_gen/train/G1314/reference/guard_fee.func.abap create mode 100644 tasks_gen/train/G1314/reference/t88_test.clas.abap create mode 100644 tasks_gen/train/G1314/seed/guard_sample.func.abap create mode 100644 tasks_gen/train/G1314/spec.md create mode 100644 tasks_gen/train/G1314/task.json create mode 100644 tasks_gen/train/G1315/generation.json create mode 100644 tasks_gen/train/G1315/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/train/G1315/reference/log_merger.clas.abap create mode 100644 tasks_gen/train/G1315/reference/log_merger.testclasses.abap create mode 100644 tasks_gen/train/G1315/spec.md create mode 100644 tasks_gen/train/G1315/task.json create mode 100644 tasks_gen/train/G1316/generation.json create mode 100644 tasks_gen/train/G1316/hidden/frame_pricer_hidden.clas.abap create mode 100644 tasks_gen/train/G1316/reference/frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1316/reference/frame_pricer.testclasses.abap create mode 100644 tasks_gen/train/G1316/seed/frame_pricer.clas.abap create mode 100644 tasks_gen/train/G1316/spec.md create mode 100644 tasks_gen/train/G1316/task.json create mode 100644 tasks_gen/train/G1317/generation.json create mode 100644 tasks_gen/train/G1317/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/train/G1317/reference/towing_fee.clas.abap create mode 100644 tasks_gen/train/G1317/reference/towing_fee.testclasses.abap create mode 100644 tasks_gen/train/G1317/spec.md create mode 100644 tasks_gen/train/G1317/task.json create mode 100644 tasks_gen/train/_logs/G1000.json create mode 100644 tasks_gen/train/_logs/G1001.json create mode 100644 tasks_gen/train/_logs/G1002.json create mode 100644 tasks_gen/train/_logs/G1003.json create mode 100644 tasks_gen/train/_logs/G1004.json create mode 100644 tasks_gen/train/_logs/G1005.json create mode 100644 tasks_gen/train/_logs/G1006.json create mode 100644 tasks_gen/train/_logs/G1007.json create mode 100644 tasks_gen/train/_logs/G1008.json create mode 100644 tasks_gen/train/_logs/G1009.json create mode 100644 tasks_gen/train/_logs/G1010.json create mode 100644 tasks_gen/train/_logs/G1011.json create mode 100644 tasks_gen/train/_logs/G1012.json create mode 100644 tasks_gen/train/_logs/G1013.json create mode 100644 tasks_gen/train/_logs/G1014.json create mode 100644 tasks_gen/train/_logs/G1015.json create mode 100644 tasks_gen/train/_logs/G1016.json create mode 100644 tasks_gen/train/_logs/G1017.json create mode 100644 tasks_gen/train/_logs/G1018.json create mode 100644 tasks_gen/train/_logs/G1019.json create mode 100644 tasks_gen/train/_logs/G1020.json create mode 100644 tasks_gen/train/_logs/G1021.json create mode 100644 tasks_gen/train/_logs/G1022.json create mode 100644 tasks_gen/train/_logs/G1023.json create mode 100644 tasks_gen/train/_logs/G1024.json create mode 100644 tasks_gen/train/_logs/G1025.json create mode 100644 tasks_gen/train/_logs/G1026.json create mode 100644 tasks_gen/train/_logs/G1027.json create mode 100644 tasks_gen/train/_logs/G1028.json create mode 100644 tasks_gen/train/_logs/G1029.json create mode 100644 tasks_gen/train/_logs/G1030.json create mode 100644 tasks_gen/train/_logs/G1031.json create mode 100644 tasks_gen/train/_logs/G1032.json create mode 100644 tasks_gen/train/_logs/G1033.json create mode 100644 tasks_gen/train/_logs/G1034.json create mode 100644 tasks_gen/train/_logs/G1035.json create mode 100644 tasks_gen/train/_logs/G1036.json create mode 100644 tasks_gen/train/_logs/G1037.json create mode 100644 tasks_gen/train/_logs/G1038.json create mode 100644 tasks_gen/train/_logs/G1039.json create mode 100644 tasks_gen/train/_logs/G1040.json create mode 100644 tasks_gen/train/_logs/G1041.json create mode 100644 tasks_gen/train/_logs/G1043.json create mode 100644 tasks_gen/train/_logs/G1046.json create mode 100644 tasks_gen/train/_logs/G1049.json create mode 100644 tasks_gen/train/_logs/G1052.json create mode 100644 tasks_gen/train/_logs/G1055.json create mode 100644 tasks_gen/train/_logs/G1300.json create mode 100644 tasks_gen/train/_logs/G1301.json create mode 100644 tasks_gen/train/_logs/G1302.json create mode 100644 tasks_gen/train/_logs/G1303.json create mode 100644 tasks_gen/train/_logs/G1304.json create mode 100644 tasks_gen/train/_logs/G1305.json create mode 100644 tasks_gen/train/_logs/G1306.json create mode 100644 tasks_gen/train/_logs/G1307.json create mode 100644 tasks_gen/train/_logs/G1308.json create mode 100644 tasks_gen/train/_logs/G1309.json create mode 100644 tasks_gen/train/_logs/G1310.json create mode 100644 tasks_gen/train/_logs/G1311.json create mode 100644 tasks_gen/train/_logs/G1312.json create mode 100644 tasks_gen/train/_logs/G1313.json create mode 100644 tasks_gen/train/_logs/G1314.json create mode 100644 tasks_gen/train/_logs/G1315.json create mode 100644 tasks_gen/train/_logs/G1316.json create mode 100644 tasks_gen/train/_logs/G1317.json create mode 100644 tasks_gen/train/plan.json diff --git a/docs/yol-haritasi.md b/docs/yol-haritasi.md index 7da1370..3e21c8f 100644 --- a/docs/yol-haritasi.md +++ b/docs/yol-haritasi.md @@ -132,6 +132,8 @@ Bitti sayılır: tek baz model seçildi, gerekçesi yazıldı. Durum (2026-10-05): 3.1 eğitim modu hazır (`harness/trainset.py`, havuz `tasks_gen/train`, eval ile çakışma kontrolü `harness/overlap.py`, 30 hata-odaklı görev dahil), üretim 12:00'de başladı (hedef 200 kabul, ~10 $ usage). 3.2 için proxy (yerel abaplint ile "save failed" ayrıntısı), yörünge kaydı (`harness/record.py`), Qwen çevirici (`train/to_qwen.py`) ve kabul süzgeci (`train/accept.py`) hazır; yörünge koşuları (`harness/trajectories.py`, 6 worker) üretim bitince. + + | 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/harness/ledger.py b/harness/ledger.py index ab7dff4..cbb06b9 100644 --- a/harness/ledger.py +++ b/harness/ledger.py @@ -35,7 +35,8 @@ def spent(since=None): def check_budget(): - limit = float(os.environ.get("BUDGET_LIMIT_USD", "45")) + # BUDGET_RESERVE_USD: ledger USD kept back (second teacher test later); the guard stops at limit - reserve + limit = float(os.environ.get("BUDGET_LIMIT_USD", "45")) - float(os.environ.get("BUDGET_RESERVE_USD", "0")) s = spent() if s >= limit: raise BudgetExceeded(f"cycle spend {s} USD >= limit {limit} USD") diff --git a/harness/pipeline.py b/harness/pipeline.py new file mode 100644 index 0000000..0235369 --- /dev/null +++ b/harness/pipeline.py @@ -0,0 +1,353 @@ +"""Stage 2 pipeline controller (rules of 2026-10-05, Opus 5.5 + Kral). + +Runs in the background; nobody polls it. It + - starts plan 2 generation (3 workers, hard and error share raised, no fixed target) when the first plan has + ended, until --gen-deadline or the budget guard; adds K (free-text) variants at about 10 % of the other tasks; + - runs trajectories with 2 workers (1 after any A4H error), at most 2 attempts per task: a second attempt only + when the first one failed or was accepted without a repair; + - writes a summary to train/STATE.md and docs/yol-haritasi.md every 50 accepted trajectories, and commits; + - stops and reports (runs/pipeline/STOPPED.txt, STOP flag for the generators) when: acceptance over the last + 30 runs is below 50 %, one harness error pattern appears 3 times, the budget guard stops, or the deadline. + + python3 -m harness.pipeline [--wait-pid PID] [--workers 2] [--gen-deadline 2026-10-10T18:00] + [--traj-deadline 2026-10-11T23:30] +Budget: BUDGET_LIMIT_USD and BUDGET_RESERVE_USD in .env (the reserve stays for a second teacher test). +""" +import argparse +import glob +import json +import os +import re +import statistics +import subprocess +import sys +import threading +import time + +from .adt_client import load_env +from . import trainset, trajectories +from .ledger import spent + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, os.path.join(ROOT, "train")) +import accept as acc # noqa: E402 (train/accept.py) + +OUT = trajectories.OUT +PIPE = os.path.join(ROOT, "runs", "pipeline") +BASE_LEDGER = 66.9665 # ledger when Part B started (2026-10-05 morning) +LEDGER_TO_USAGE = 2.7 +ALLOWANCE_USAGE = 35.0 # until 11 October, without asking (Kral) +MARKER = "" +GIT_ENV = dict(os.environ, GIT_AUTHOR_NAME="Kral", GIT_AUTHOR_EMAIL="kral@local", + GIT_COMMITTER_NAME="Kral", GIT_COMMITTER_EMAIL="kral@local") +HARNESS_MATCH = acc.HARNESS_ERRORS + + +def log(*a): + print(time.strftime("%F %T"), *a, flush=True) + + +def ts(text): + return time.mktime(time.strptime(text, "%Y-%m-%dT%H:%M")) + + +class Pipeline: + def __init__(self, a): + self.a = a + self.lock = threading.Lock() + self.stop = threading.Event() + self.reason = None + self.max_workers = a.workers + self.inflight = set() + self.cache = {} # run_dir -> evaluation + self.events = {} # harness error key -> count + self.task_meta = {} + self.gen_deadline, self.traj_deadline = ts(a.gen_deadline), ts(a.traj_deadline) + self.children = [] + rows = self.rows() + for r in rows: + self.evaluate(r) + self.milestone = self.accepted_total() // 50 + + # ---- data ----------------------------------------------------------------------------------------------- + def rows(self): + p = os.path.join(OUT, "summary.jsonl") + return [json.loads(l) for l in open(p)] if os.path.exists(p) else [] + + def meta(self, task_id): + if task_id not in self.task_meta: + try: + t = json.load(open(os.path.join(trajectories.POOL, task_id, "task.json"))) + except OSError: + t = {} + self.task_meta[task_id] = {"category": t.get("category"), "object_type": t.get("object_type")} + return self.task_meta[task_id] + + def evaluate(self, row): + """{accepted, repair, reasons, event_keys, hints, tokens-free} of one summary row (cached).""" + key = row.get("run_dir") or f"{row['task']}_{row['attempt']}" + if key in self.cache: + return self.cache[key] + ev = {"task": row["task"], "attempt": row["attempt"], "accepted": False, "repair": False, + "reasons": [], "events": [], "hints": 0} + path = os.path.join(OUT, row.get("run_dir") or "-", "record.json") + if row.get("harness_error"): + ev["reasons"].append("harness_error") + ev["events"].append("harness:" + str(row.get("error"))[:60]) + if row.get("setup_failed"): + ev["reasons"].append("setup_failed") + ev["events"].append("setup_failed") + if os.path.exists(path): + rec = json.load(open(path)) + ok, why = acc.judge(rec, row, self.a.min_score) + ev["accepted"], ev["reasons"] = ok, why or ev["reasons"] + ev["hints"] = rec["metadata"].get("syntax_hints") or 0 + if ok: + ev["repair"] = acc.has_repair(acc.trim_loops(rec["messages"])[0]) + for r in rec["tool_results_raw"]: + m = HARNESS_MATCH.search(r.get("result") or "") + if m and not r.get("tool", "").startswith("sap_run_unit"): + ev["events"].append("text:" + m.group(0)[:40]) + break + if row.get("teardown_ok") is False: + ev["events"].append("teardown") + elif not ev["reasons"]: + ev["reasons"].append("no_record") + self.cache[key] = ev + return ev + + def evaluated(self): + return [self.evaluate(r) for r in self.rows()] + + def accepted_total(self): + return sum(e["accepted"] for e in self.evaluated()) + + # ---- jobs ----------------------------------------------------------------------------------------------- + def next_job(self): + with self.lock: + rows = self.rows() + done = {(r["task"], r["attempt"]) for r in rows} + ev0 = {r["task"]: self.evaluate(r) for r in rows if r["attempt"] == 0} + tasks = trajectories.accepted_tasks() + for t in tasks: # first attempt for every task first + if (t, 0) not in done and (t, 0) not in self.inflight: + self.inflight.add((t, 0)) + return t, 0 + for t in tasks: # second attempt: first failed, or accepted without a repair + e = ev0.get(t) + if e and (t, 1) not in done and (t, 1) not in self.inflight and (not e["accepted"] or not e["repair"]): + self.inflight.add((t, 1)) + return t, 1 + return None + + def worker(self, i): + while not self.stop.is_set(): + if time.time() > self.traj_deadline: + self.halt("trajectory deadline reached") + return + if i >= self.max_workers: + time.sleep(30) + continue + job = self.next_job() + if not job: + if self.generation_alive() or self.inflight: + time.sleep(60) + continue + self.halt("no job left and generation ended") + return + trajectories.one(job[0], job[1], self.stop) + with self.lock: + self.inflight.discard(job) + self.after_run(job) + if self.stop.is_set() and not self.reason: + self.halt("budget guard stopped a run") + + # ---- rules ---------------------------------------------------------------------------------------------- + def after_run(self, job): + rows = self.rows() + row = next((r for r in reversed(rows) if (r["task"], r["attempt"]) == job), None) + if row is None: + return + ev = self.evaluate(row) + for k in ev["events"]: + self.events[k] = self.events.get(k, 0) + 1 + log("A4H/harness event:", k, "x", self.events[k], "->", row.get("run_dir")) + if self.max_workers > 1: + self.max_workers = 1 + log("harness error seen: trajectory workers 2 -> 1") + if self.events[k] >= 3: + self.halt(f"harness error pattern repeated 3 times: {k}") + evs = self.evaluated() + if len(evs) >= 30: + rate = sum(e["accepted"] for e in evs[-30:]) / 30 + if rate < 0.5: + self.halt(f"acceptance {rate:.0%} over the last 30 runs (below 50 %)") + try: + from .ledger import check_budget + check_budget() + except Exception as e: # noqa: BLE001 BudgetExceeded + self.halt(f"budget guard: {e}") + total = sum(e["accepted"] for e in evs) + while total // 50 > self.milestone and not self.reason: + self.milestone += 1 + self.summary(self.milestone * 50) + + def halt(self, reason): + if self.stop.is_set() and self.reason: + return + self.reason = reason + self.stop.set() + os.makedirs(PIPE, exist_ok=True) + open(trainset.STOP_FLAG, "w").write(reason + "\n") # generators end after their current slot + log("STOP:", reason) + + # ---- generation side -------------------------------------------------------------------------------------- + def generation_alive(self): + if self.reason: + return False + out = subprocess.run(["pgrep", "-f", r"harness\.trainset (run|k)"], capture_output=True, text=True).stdout + return bool(out.split()) or time.time() < self.gen_deadline + + def old_generators(self): + out = subprocess.run(["pgrep", "-fl", r"harness\.trainset run --part"], capture_output=True, text=True).stdout + return [l for l in out.splitlines() if "--plan plan2" not in l] + + def supervisor(self): + launched = False + k_proc = None + crashes = 0 + while not self.stop.is_set(): + time.sleep(60) + if time.time() > self.gen_deadline or os.path.exists(trainset.STOP_FLAG): + continue + if not launched and not self.old_generators(): + trainset.ensure_plan("plan2") + dl = self.a.gen_deadline + for i in range(3): + f = open(os.path.join(ROOT, "runs", "gen_train", f"p2_w{i}.log"), "a") + self.children.append(subprocess.Popen( + [sys.executable, "-m", "harness.trainset", "run", "--plan", "plan2", "--part", str(i), + "--parts", "3", "--deadline", dl], cwd=ROOT, stdout=f, stderr=f)) + launched = True + log("plan 2 generators started (3 workers, deadline", dl + ")") + elif launched: + for i, c in enumerate(self.children): + if c.poll() not in (None, 0) and crashes < 3: + crashes += 1 + log("generator", i, "exited with", c.returncode, "- restarted") + f = open(os.path.join(ROOT, "runs", "gen_train", f"p2_w{i}.log"), "a") + self.children[i] = subprocess.Popen( + [sys.executable, "-m", "harness.trainset", "run", "--plan", "plan2", "--part", str(i), + "--parts", "3", "--deadline", self.a.gen_deadline], cwd=ROOT, stdout=f, stderr=f) + # K variants: free text, about 10 % of the other accepted training tasks + if k_proc is None or k_proc.poll() is not None: + logs = [json.load(open(f)) for f in glob.glob(os.path.join(trainset.POOL, "_logs", "G*.json"))] + base = sum(1 for l in logs if l.get("accepted") and l.get("category") != "K") + have = sum(1 for l in logs if l.get("category") == "K") + want = min(99, int(0.1 * base)) + if want > have: + f = open(os.path.join(ROOT, "runs", "gen_train", "k.log"), "a") + k_proc = subprocess.Popen([sys.executable, "-m", "harness.trainset", "k", "--k-count", str(want)], + cwd=ROOT, stdout=f, stderr=f) + + # ---- summary ---------------------------------------------------------------------------------------------- + def summary(self, n): + log("summary at", n, "accepted trajectories") + subprocess.run([sys.executable, "train/accept.py", "--min-score", str(self.a.min_score)], cwd=ROOT, + capture_output=True) + vp = os.path.join(ROOT, "train", ".venv", "bin", "python") + subprocess.run([vp, "train/to_qwen.py", os.path.join(OUT, "accepted.jsonl"), os.path.join(OUT, "qwen.jsonl")], + cwd=ROOT, capture_output=True) + toks = sorted(json.loads(l)["n_tokens"] for l in open(os.path.join(OUT, "qwen.jsonl"))) + pct = lambda p: toks[min(len(toks) - 1, int(p * len(toks)))] if toks else None # noqa: E731 + rows, evs = self.rows(), self.evaluated() + logs = [json.load(open(f)) for f in glob.glob(os.path.join(trainset.POOL, "_logs", "G*.json"))] + acc_t = sum(1 for l in logs if l.get("accepted")) + accepted = [e for e in evs if e["accepted"]] + repairs = sum(e["repair"] for e in accepted) + + def table(keyname): + agg = {} + for e in evs: + k = self.meta(e["task"])[keyname] or "?" + a = agg.setdefault(k, [0, 0, 0]) + a[0] += 1 + a[1] += e["accepted"] + a[2] += e["accepted"] and e["repair"] + return "; ".join(f"{k} {v[1]}/{v[0]} (repair {v[2]})" for k, v in sorted(agg.items())) + s = spent() + used = (s - BASE_LEDGER) / LEDGER_TO_USAGE + guard = float(os.environ.get("BUDGET_LIMIT_USD", "0")) - float(os.environ.get("BUDGET_RESERVE_USD", "0")) + text = f"""### Stage 2 summary at {n} accepted trajectories ({time.strftime('%Y-%m-%d %H:%M')}) + +- Tasks: {acc_t} accepted of {len(logs)} generated (K variants {sum(1 for l in logs if l.get('category') == 'K')}); \ +trajectory runs {len(rows)}, accepted trajectories {len(accepted)} (acceptance {len(accepted) / max(len(rows), 1):.0%}). +- Budget: used {used:.1f} usage (ledger {s - BASE_LEDGER:.1f}) since 2026-10-05; left to the guard {(guard - s) / LEDGER_TO_USAGE:.1f} usage \ +(ledger {guard - s:.1f}; reserve {os.environ.get('BUDGET_RESERVE_USD', '0')} ledger kept); allowance until 11 October {ALLOWANCE_USAGE - used:.1f} usage. +- Repair share (accepted trajectories with an error followed by a fix): {repairs}/{len(accepted)} = {repairs / max(len(accepted), 1):.0%}. +- By category, accepted/runs: {table('category')}. +- By object type, accepted/runs: {table('object_type')}. +- Tokens of accepted samples (20 tool schemas kept): p50 {pct(0.5)}, p90 {pct(0.9)}, p95 {pct(0.95)}, max {toks[-1] if toks else None}, n {len(toks)}. +- Syntax hints (proxy syntaxCheck added): {sum(e['hints'] for e in evs)} in {sum(1 for e in evs if e['hints'])} runs. +- Harness events: {sum(self.events.values())} ({self.events or 'none'}); trajectory workers now {self.max_workers}. +""" + if getattr(self.a, "dry_summary", False): + print(text) + return + with open(os.path.join(ROOT, "train", "STATE.md"), "a") as f: + f.write("\n" + text) + p = os.path.join(ROOT, "docs", "yol-haritasi.md") + d = open(p).read() + d = d.replace(MARKER, MARKER + "\n\n" + text, 1) if MARKER in d else d + "\n" + text + open(p, "w").write(d) + subprocess.run(["git", "add", "train/STATE.md", "docs/yol-haritasi.md", "tasks_gen/train", "harness", "train"], + cwd=ROOT, env=GIT_ENV, capture_output=True) + subprocess.run(["git", "commit", "-q", "-m", f"Stage 2 summary at {n} accepted trajectories\n\n" + "Co-Authored-By: Claude Sonnet 5.5 "], + cwd=ROOT, env=GIT_ENV, capture_output=True) + + # ---- main ------------------------------------------------------------------------------------------------- + def run(self): + os.makedirs(PIPE, exist_ok=True) + if os.path.exists(trainset.STOP_FLAG): + os.remove(trainset.STOP_FLAG) + log("pipeline start: workers", self.max_workers, "accepted", self.accepted_total(), "spent", spent()) + threads = [threading.Thread(target=self.worker, args=(i,)) for i in range(self.a.workers)] + threading.Thread(target=self.supervisor, daemon=True).start() + for t in threads: + t.start() + for t in threads: + t.join() + evs = self.evaluated() + text = (f"reason: {self.reason}\ntime: {time.strftime('%F %T')}\nruns: {len(evs)}, accepted " + f"{sum(e['accepted'] for e in evs)}\nspent ledger: {spent()}\nevents: {self.events}\n") + open(os.path.join(PIPE, "STOPPED.txt"), "w").write(text) + log("pipeline ended:", text.replace("\n", " | ")) + + +def main(): + load_env(os.path.join(ROOT, ".env")) + ap = argparse.ArgumentParser() + ap.add_argument("--wait-pid", type=int, help="start when this process has ended (the first trajectory batch)") + ap.add_argument("--workers", type=int, default=2) + ap.add_argument("--min-score", type=float, default=80) + ap.add_argument("--gen-deadline", default="2026-10-10T18:00") + ap.add_argument("--traj-deadline", default="2026-10-11T23:30") + ap.add_argument("--dry-summary", action="store_true", help="print the summary text and exit (no file, no commit)") + a = ap.parse_args() + if a.dry_summary: + Pipeline(a).summary(0) + return + if a.wait_pid: + while True: + try: + os.kill(a.wait_pid, 0) + except OSError: + break + time.sleep(30) + os.makedirs(OUT, exist_ok=True) + Pipeline(a).run() + + +if __name__ == "__main__": + main() diff --git a/harness/trainset.py b/harness/trainset.py index db6d95b..654fe54 100644 --- a/harness/trainset.py +++ b/harness/trainset.py @@ -46,13 +46,16 @@ CATEGORY_SHARE = {"A": 10, "B": 15, "C": 15, "D": 10, "E": 15, "F": 10, "G": 10, ERROR_CATEGORY = {"named-type": "A", "reserved-word": "D", "long-names": "C"} -def build_plan(): - """Category shares as in the eval plan; object types inside a category as in evalset.SLOTS.""" +def build_plan(n_slots=N_SLOTS, n_error=N_ERROR, hard_share=0.0, first_id=FIRST_ID, run_base=RUN_BASE): + """Category shares as in the eval plan; object types inside a category as in evalset.SLOTS. + n_error slots target the common errors (named-type, reserved-word, long-names); hard_share of the other + slots get difficulty 3 (every 10th slot pattern, deterministic).""" share_sum = sum(CATEGORY_SHARE.values()) items = [] for cat, share in CATEGORY_SHARE.items(): - k_cat = round(N_SLOTS * share / share_sum) - k_err = sum(1 for k in ERROR_CATEGORY if ERROR_CATEGORY[k] == cat) * N_ERROR // 3 + k_cat = round(n_slots * share / share_sum) + per_kind = n_error // 3 + k_err = sum(1 for k in ERROR_CATEGORY if ERROR_CATEGORY[k] == cat) * per_kind slots = [x for x in SLOTS if x[0] == cat] weight = sum(n for _, _, n, _ in slots) k_rest = k_cat - k_err @@ -69,16 +72,19 @@ def build_plan(): for kind, text in ERROR_HINTS: if ERROR_CATEGORY[kind] != cat: continue - for i in range(N_ERROR // 3): + for i in range(per_kind): t = ERROR_TYPES[i % len(ERROR_TYPES)] pos += 1 items.append({"category": cat, "object_type": t, "topic": text, "error_kind": kind, "frac": pos / (k_cat + 1)}) items.sort(key=lambda x: (x["frac"], x["category"], x["object_type"])) # interleave: a cut keeps the mix + hard_every = round(1 / hard_share) if hard_share else 0 for n, it in enumerate(items): it.pop("frac") - it["id"] = f"G{FIRST_ID + n:04d}" - it["run_base"] = RUN_BASE + 40 * n + it["id"] = f"G{first_id + n:04d}" + it["run_base"] = run_base + 40 * n + if hard_every and it["category"] != "H" and n % hard_every == hard_every // 2: + it["difficulty"] = 3 return items @@ -92,12 +98,42 @@ def accepted_count(): return n -def run(part, parts, target, stop_ledger): +STOP_FLAG = os.path.join(ROOT, "runs", "pipeline", "STOP") # the pipeline writes it: workers end after the current slot +BACKLOG_LIMIT = 40 # accepted tasks without a first trajectory run: generation waits above this +PLAN2 = {"n_slots": 500, "n_error": 100, "hard_share": 0.3, "first_id": 1400, "run_base": 100000} +# plan 2 (2026-10-05, Opus 5.5 rules): error tasks 20 % (plan 1: 13 %, +50 %), hard (difficulty 3) 30 % of the +# other slots (plan 1: 20 % assumed from the pilot hard batch), same category shares, ids G1400+, no fixed target + + +def plan_path(name): + return os.path.join(POOL, name + ".json") + + +def ensure_plan(name, stop_ledger=27.0, target=200): os.makedirs(POOL, exist_ok=True) - if not os.path.exists(PLAN): - json.dump({"slots": build_plan(), "ledger_at_start": spent(), "stop_ledger": stop_ledger, - "target": target, "created": time.strftime("%F %T")}, open(PLAN, "w"), indent=1) - plan = json.load(open(PLAN)) + if not os.path.exists(plan_path(name)): + if name == "plan": + slots, tgt, stop = build_plan(), target, stop_ledger + else: + slots, tgt, stop = build_plan(**PLAN2), None, 1e6 # only the budget guard and the deadline stop it + json.dump({"slots": slots, "ledger_at_start": spent(), "stop_ledger": stop, "target": tgt, + "created": time.strftime("%F %T")}, open(plan_path(name), "w"), indent=1) + return json.load(open(plan_path(name))) + + +def backlog(): + """Accepted training tasks that have no first trajectory run yet.""" + done = set() + sp = os.path.join(ROOT, "runs", "traj", "summary.jsonl") + if os.path.exists(sp): + done = {json.loads(l)["task"] for l in open(sp) if json.loads(l)["attempt"] == 0} + acc = {os.path.basename(os.path.dirname(f)) for f in glob.glob(os.path.join(POOL, "G*", "generation.json")) + if json.load(open(f)).get("accepted")} + return len(acc - done) + + +def run(part, parts, target, stop_ledger, plan_name="plan", deadline=None): + plan = ensure_plan(plan_name, stop_ledger, target) stop_at = plan["ledger_at_start"] + plan["stop_ledger"] base_url = os.environ.get("LLM_BASE_URL", "http://127.0.0.1:11434/v1") evals = overlap.load_pool("eval") @@ -106,7 +142,16 @@ def run(part, parts, target, stop_ledger): continue if os.path.exists(os.path.join(POOL, "_logs", s["id"] + ".json")): continue # done or failed: not tried again (delete the log to retry) - if accepted_count() >= plan["target"]: + while backlog() > BACKLOG_LIMIT and not os.path.exists(STOP_FLAG) \ + and not (deadline and time.time() > deadline): + time.sleep(120) # trajectories are the slower side: do not generate tasks that wait for days + if os.path.exists(STOP_FLAG): + print("STOP flag", flush=True) + break + if deadline and time.time() > deadline: + print("DEADLINE", flush=True) + break + if plan["target"] and accepted_count() >= plan["target"]: print("TARGET reached", flush=True) break if spent() >= stop_at: @@ -124,14 +169,15 @@ def run(part, parts, target, stop_ledger): f"names {sc['name']:.2f}). Choose a different business topic and different object names." for i, sc in hits[:3]] try: - log = generate(s["id"], "train", s["object_type"], s["category"], 2, "deepseek-v4.1-flash:cloud", + log = generate(s["id"], "train", s["object_type"], s["category"], s.get("difficulty", 2), + "deepseek-v4.1-flash:cloud", base_url, s["run_base"], topic, extra_check=extra) except BudgetExceeded as e: print("BUDGET", e, flush=True) break except Exception as e: # noqa: BLE001 log = {"id": s["id"], "error": str(e)[:500]} - log.update(error_kind=s.get("error_kind"), spent_total=spent()) + log.update(error_kind=s.get("error_kind"), difficulty=s.get("difficulty", 2), spent_total=spent()) os.makedirs(os.path.join(POOL, "_logs"), exist_ok=True) json.dump(log, open(os.path.join(POOL, "_logs", s["id"] + ".json"), "w"), indent=1) stray = os.path.join(POOL, "generation.json") # generate() writes here when no task dir exists @@ -156,7 +202,7 @@ def run_k(count): continue used = {json.load(open(f)).get("base_task") for f in glob.glob(os.path.join(POOL, "_logs", "G13*.json"))} cands = [] # accepted, not K, not H (a stop task has no free-text form), not used yet - for f in sorted(glob.glob(os.path.join(POOL, "_logs", "G1[0-2]*.json"))): + for f in sorted(glob.glob(os.path.join(POOL, "_logs", "G1*.json"))): lg = json.load(open(f)) if lg.get("accepted") and lg.get("category") not in ("H", "K") and lg["id"] not in used: cands.append(lg) @@ -170,8 +216,7 @@ def run_k(count): otype = min(kinds, key=lambda t: done_types.count(t)) src = kinds[otype][0] style = K_STYLES_CYCLE[n % 2] - if spent() >= json.load(open(PLAN))["ledger_at_start"] + json.load(open(PLAN))["stop_ledger"]: - print("PHASE LIMIT", flush=True) + if os.path.exists(STOP_FLAG): return try: log = make_k_variant(src["id"], new_id, style, "deepseek-v4.1-flash:cloud", base_url, @@ -195,6 +240,9 @@ def main(): ap.add_argument("--part", type=int, default=0) ap.add_argument("--parts", type=int, default=1) ap.add_argument("--target", type=int, default=200) + ap.add_argument("--plan", default="plan", help="plan (first 223 slots) or plan2 (hard and error share raised)") + ap.add_argument("--deadline", help="YYYY-MM-DDTHH:MM local time: no new slot after it") + ap.add_argument("--k-count", type=int, default=K_COUNT) ap.add_argument("--stop-ledger", type=float, default=27.0, help="ledger USD for this phase (10 USD usage = 27)") a = ap.parse_args() if a.cmd == "plan": @@ -204,9 +252,10 @@ def main(): print(collections.Counter(x["object_type"] for x in p), collections.Counter(x.get("error_kind") for x in p)) return if a.cmd == "k": - run_k(K_COUNT) + run_k(a.k_count) return - run(a.part, a.parts, a.target, a.stop_ledger) + dl = time.mktime(time.strptime(a.deadline, "%Y-%m-%dT%H:%M")) if a.deadline else None + run(a.part, a.parts, a.target, a.stop_ledger, a.plan, dl) if __name__ == "__main__": diff --git a/harness/trajectories.py b/harness/trajectories.py index ba0aeb8..fc4a310 100644 --- a/harness/trajectories.py +++ b/harness/trajectories.py @@ -24,7 +24,7 @@ from .runner import Runner ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) POOL = os.path.join(ROOT, "tasks_gen", "train") OUT = os.path.join(ROOT, "runs", "traj") -RUN_BASE = 41000 # 41000 + (task number - 1000) * 3 + attempt; below 36**3 * ... (prefix rule) +RUN_BASE = 200000 # 200000 + (task number - 1000) * 3 + attempt (a digit must lead the 4-char base36 run: < 466560) MODEL = "deepseek-v4.1-flash:cloud" LOCK = threading.Lock() diff --git a/tasks_gen/train/G1000/faulty/m0_i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/faulty/m0_i_cont_weight.ddls.asddls new file mode 100644 index 0000000..ab5308a --- /dev/null +++ b/tasks_gen/train/G1000/faulty/m0_i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id <> cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1000/faulty/m1_i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/faulty/m1_i_cont_weight.ddls.asddls new file mode 100644 index 0000000..0534645 --- /dev/null +++ b/tasks_gen/train/G1000/faulty/m1_i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg + cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1000/faulty/m2_i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/faulty/m2_i_cont_weight.ddls.asddls new file mode 100644 index 0000000..1c81bf5 --- /dev/null +++ b/tasks_gen/train/G1000/faulty/m2_i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg <= cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1000/faulty/m3_i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/faulty/m3_i_cont_weight.ddls.asddls new file mode 100644 index 0000000..1ef92ef --- /dev/null +++ b/tasks_gen/train/G1000/faulty/m3_i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 101, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1000/faulty/m4_i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/faulty/m4_i_cont_weight.ddls.asddls new file mode 100644 index 0000000..ea0028d --- /dev/null +++ b/tasks_gen/train/G1000/faulty/m4_i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'ZOAD' + end as status +} diff --git a/tasks_gen/train/G1000/generation.json b/tasks_gen/train/G1000/generation.json new file mode 100644 index 0000000..e0daa3f --- /dev/null +++ b/tasks_gen/train/G1000/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1000", + "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/G1000/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1000/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..6a36d64 --- /dev/null +++ b/tasks_gen/train/G1000/hidden/t20_hidden.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}t20_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 deviation_below FOR TESTING. + METHODS deviation_zero FOR TESTING. + METHODS deviation_above FOR TESTING. + METHODS percent_rounded FOR TESTING. + METHODS status_hold FOR TESTING. + METHODS status_warn FOR TESTING. + METHODS status_load FOR TESTING. + METHODS status_at_five_pct FOR TESTING. + METHODS unweighed_hidden FOR TESTING. + METHODS customer_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CONT_WEIGHT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_cont TYPE STANDARD TABLE OF {{p}}cont WITH EMPTY KEY. + DATA lt_weigh TYPE STANDARD TABLE OF {{p}}weigh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_cont = VALUE #( + ( cont_id = 'C1' customer = 'ALPHA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C2' customer = 'BETA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C3' customer = 'GAMMA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C4' customer = 'DELTA' decl_kg = '500.00' max_kg = '600.00' ) + ( cont_id = 'C5' customer = 'EPSILON' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C6' customer = 'ZETA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C7' customer = 'ETA' decl_kg = '800.00' max_kg = '900.00' ) + ( cont_id = 'C8' customer = 'THETA' decl_kg = '300.00' max_kg = '400.00' ) ). + lt_weigh = VALUE #( + ( cont_id = 'C1' meas_kg = '1020.00' ) + ( cont_id = 'C2' meas_kg = '1100.00' ) + ( cont_id = 'C3' meas_kg = '1300.00' ) + ( cont_id = 'C4' meas_kg = '450.00' ) + ( cont_id = 'C5' meas_kg = '950.00' ) + ( cont_id = 'C7' meas_kg = '800.00' ) + ( cont_id = 'C8' meas_kg = '310.00' ) ). + go_env->insert_test_data( lt_cont ). + go_env->insert_test_data( lt_weigh ). + ENDMETHOD. + + METHOD deviation_below. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C4' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '50.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD deviation_zero. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C7' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD deviation_above. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C1' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD percent_rounded. + SELECT SINGLE deviation_pct FROM {{p}}i_cont_weight WHERE cont_id = 'C8' INTO @DATA(lv_pct). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.33' ) + act = CONV decfloat34( lv_pct ) ). + ENDMETHOD. + + METHOD status_hold. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C3' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'HOLD' act = lv_status ). + ENDMETHOD. + + METHOD status_warn. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C2' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'WARN' act = lv_status ). + ENDMETHOD. + + METHOD status_load. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C1' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = lv_status ). + ENDMETHOD. + + METHOD status_at_five_pct. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C5' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = lv_status ). + ENDMETHOD. + + METHOD unweighed_hidden. + SELECT COUNT(*) FROM {{p}}i_cont_weight INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lv_rows ). + ENDMETHOD. + + METHOD customer_shown. + SELECT SINGLE customer FROM {{p}}i_cont_weight WHERE cont_id = 'C4' INTO @DATA(lv_cust). + cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = lv_cust ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1000/mutation.json b/tasks_gen/train/G1000/mutation.json new file mode 100644 index 0000000..3c98f8a --- /dev/null +++ b/tasks_gen/train/G1000/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1000", + "mutants": [ + { + "object": "{{P}}I_CONT_WEIGHT", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "DEVIATION_ABOVE", + "DEVIATION_BELOW", + "DEVIATION_ZERO", + "PERCENT_ROUNDED", + "STATUS_HOLD", + "STATUS_LOAD", + "STATUS_WARN", + "UNWEIGHED_HIDDEN" + ] + }, + { + "object": "{{P}}I_CONT_WEIGHT", + "mutant": "line 12: - -> + (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "PERCENT_ROUNDED" + ] + }, + { + "object": "{{P}}I_CONT_WEIGHT", + "mutant": "line 14: > -> <= (rel)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "STATUS_AT_FIVE_PCT", + "STATUS_HOLD", + "STATUS_LOAD", + "STATUS_WARN" + ] + }, + { + "object": "{{P}}I_CONT_WEIGHT", + "mutant": "line 15: 100 -> 101 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "STATUS_AT_FIVE_PCT" + ] + }, + { + "object": "{{P}}I_CONT_WEIGHT", + "mutant": "line 16: 'LOAD' -> 'ZOAD' (lit)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "STATUS_AT_FIVE_PCT", + "STATUS_LOAD" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1000/reference/i_cont_weight.ddls.asddls b/tasks_gen/train/G1000/reference/i_cont_weight.ddls.asddls new file mode 100644 index 0000000..db6495d --- /dev/null +++ b/tasks_gen/train/G1000/reference/i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1000/reference/t20_test.clas.abap b/tasks_gen/train/G1000/reference/t20_test.clas.abap new file mode 100644 index 0000000..bdc4ffd --- /dev/null +++ b/tasks_gen/train/G1000/reference/t20_test.clas.abap @@ -0,0 +1,53 @@ +CLASS {{p}}t20_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 hold_for_heavy FOR TESTING. + METHODS load_for_light FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CONT_WEIGHT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_cont TYPE STANDARD TABLE OF {{p}}cont WITH EMPTY KEY. + DATA lt_weigh TYPE STANDARD TABLE OF {{p}}weigh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_cont = VALUE #( ( cont_id = 'A' customer = 'CUST_A' decl_kg = '1000.00' max_kg = '1100.00' ) + ( cont_id = 'B' customer = 'CUST_B' decl_kg = '1000.00' max_kg = '1100.00' ) ). + lt_weigh = VALUE #( ( cont_id = 'A' meas_kg = '1150.00' ) + ( cont_id = 'B' meas_kg = '1010.00' ) ). + go_env->insert_test_data( lt_cont ). + go_env->insert_test_data( lt_weigh ). + ENDMETHOD. + + METHOD hold_for_heavy. + SELECT SINGLE deviation_kg, deviation_pct, status FROM {{p}}i_cont_weight + WHERE cont_id = 'A' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '150.00' ) + act = CONV decfloat34( ls_row-deviation_kg ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.00' ) + act = CONV decfloat34( ls_row-deviation_pct ) ). + cl_abap_unit_assert=>assert_equals( exp = 'HOLD' act = ls_row-status ). + ENDMETHOD. + + METHOD load_for_light. + SELECT SINGLE deviation_kg, deviation_pct, status FROM {{p}}i_cont_weight + WHERE cont_id = 'B' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) + act = CONV decfloat34( ls_row-deviation_kg ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) + act = CONV decfloat34( ls_row-deviation_pct ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = ls_row-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1000/seed/cont.tabl.asabap b/tasks_gen/train/G1000/seed/cont.tabl.asabap new file mode 100644 index 0000000..d513411 --- /dev/null +++ b/tasks_gen/train/G1000/seed/cont.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Container' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}cont { + key client : abap.clnt not null; + key cont_id : abap.char(10) not null; + customer : abap.char(10); + decl_kg : abap.dec(15,2); + max_kg : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1000/seed/weigh.tabl.asabap b/tasks_gen/train/G1000/seed/weigh.tabl.asabap new file mode 100644 index 0000000..8b2b887 --- /dev/null +++ b/tasks_gen/train/G1000/seed/weigh.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Weighbridge record' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}weigh { + key client : abap.clnt not null; + key cont_id : abap.char(10) not null; + meas_kg : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1000/spec.md b/tasks_gen/train/G1000/spec.md new file mode 100644 index 0000000..cf76f35 --- /dev/null +++ b/tasks_gen/train/G1000/spec.md @@ -0,0 +1,41 @@ +# 1. Goal +Show the weight check of each container at a container terminal. A report and an +OData service will read the check, so the calculation must be in the database, +in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}CONT (container master), package $TMP. Key: CONT_ID (CHAR 10). + Fields: CUSTOMER (CHAR 10), DECL_KG (DEC 15,2, declared weight in kilograms), + MAX_KG (DEC 15,2, maximum permitted weight in kilograms). +- Table {{P}}WEIGH (weighbridge record), package $TMP. Key: CONT_ID (CHAR 10). + Field: MEAS_KG (DEC 15,2, measured weight in kilograms). + +# 4. Contract +- Create the CDS view entity {{P}}I_CONT_WEIGHT in package $TMP. +- Elements, with these names: CONT_ID (key), CUSTOMER, DECL_KG, MEAS_KG, + DEVIATION_KG, DEVIATION_PCT, STATUS. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each container that has a weighbridge record. Do not show a + container without a weighbridge record. +2. DEVIATION_KG is the difference between MEAS_KG and DECL_KG, without a sign. + It is always positive or zero and it has 2 decimals. +3. DEVIATION_PCT is DEVIATION_KG as a percentage of DECL_KG. It has 2 decimals. +4. STATUS is 'HOLD' if MEAS_KG is greater than MAX_KG. +5. STATUS is 'WARN' if rule 4 is not true and DEVIATION_PCT is greater than 5. +6. STATUS is 'LOAD' in all other cases. +7. DECL_KG is always greater than zero. + +# 6. Constraints +- Release target: 8.16. +- Use Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change {{P}}CONT and {{P}}WEIGH. + +# 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/G1000/task.json b/tasks_gen/train/G1000/task.json new file mode 100644 index 0000000..220f577 --- /dev/null +++ b/tasks_gen/train/G1000/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1000", + "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}}CONT", + "file": "seed/cont.tabl.asabap", + "description": "Container master" + }, + { + "type": "TABL", + "name": "{{P}}WEIGH", + "file": "seed/weigh.tabl.asabap", + "description": "Weighbridge record" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_CONT_WEIGHT", + "fields": [ + "CONT_ID", + "CUSTOMER", + "DECL_KG", + "MEAS_KG", + "DEVIATION_KG", + "DEVIATION_PCT", + "STATUS" + ] + } + ], + "out_of_scope": [ + "{{P}}CONT", + "{{P}}WEIGH" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "T20 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_CONT_WEIGHT", + "file": "reference/i_cont_weight.ddls.asddls", + "description": "Container weight check" + }, + { + "type": "CLAS", + "name": "{{P}}T20_TEST", + "file": "reference/t20_test.clas.abap", + "description": "T20 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1001/faulty/m0_harvest_evaluator.clas.abap b/tasks_gen/train/G1001/faulty/m0_harvest_evaluator.clas.abap new file mode 100644 index 0000000..b167ec5 --- /dev/null +++ b/tasks_gen/train/G1001/faulty/m0_harvest_evaluator.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}harvest_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS evaluate + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE {{p}}if_harvest=>tt_member + RETURNING VALUE(rt_result) TYPE {{p}}if_harvest=>tt_result. + + PRIVATE SECTION. + TYPES tt_member_key TYPE HASHED TABLE OF {{p}}if_harvest=>ty_member + WITH UNIQUE KEY member_id. + TYPES tt_member_id TYPE SORTED TABLE OF {{p}}if_harvest=>ty_member_id + WITH UNIQUE KEY table_line. + TYPES tt_report_key TYPE SORTED TABLE OF {{p}}if_harvest=>ty_report + WITH NON-UNIQUE KEY variety member_id. + + METHODS countable_reports + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE tt_member_key + RETURNING VALUE(rt_report) TYPE tt_report_key. + METHODS is_countable + IMPORTING is_report TYPE {{p}}if_harvest=>ty_report + it_member TYPE tt_member_key + RETURNING VALUE(rv_count) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}harvest_evaluator IMPLEMENTATION. + METHOD evaluate. + DATA lt_member TYPE tt_member_key. + lt_member = it_member. + + DATA(lt_report) = countable_reports( it_report = it_report + it_member = lt_member ). + + DATA lt_result TYPE {{p}}if_harvest=>tt_result. + DATA lt_member_id TYPE tt_member_id. + LOOP AT lt_report ASSIGNING FIELD-SYMBOL() GROUP BY -variety. + DATA(lv_variety) = -variety. + DATA(lv_total) = 0. + CLEAR lt_member_id. + LOOP AT GROUP ASSIGNING FIELD-SYMBOL(). + lv_total = lv_total - -quantity_kg. + INSERT -member_id INTO TABLE lt_member_id. + ENDLOOP. + APPEND VALUE {{p}}if_harvest=>ty_result( + variety = lv_variety + total_kg = lv_total + member_count = lines( lt_member_id ) ) TO lt_result. + ENDLOOP. + + SORT lt_result BY total_kg DESCENDING variety ASCENDING. + rt_result = lt_result. + ENDMETHOD. + + METHOD countable_reports. + LOOP AT it_report INTO DATA(ls_report). + IF is_countable( is_report = ls_report + it_member = it_member ) = abap_false. + CONTINUE. + ENDIF. + INSERT ls_report INTO TABLE rt_report. + ENDLOOP. + ENDMETHOD. + + METHOD is_countable. + IF is_report-quantity_kg <= 0. + RETURN. + ENDIF. + rv_count = xsdbool( line_exists( it_member[ member_id = is_report-member_id ] ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/faulty/m2_harvest_evaluator.clas.abap b/tasks_gen/train/G1001/faulty/m2_harvest_evaluator.clas.abap new file mode 100644 index 0000000..8583669 --- /dev/null +++ b/tasks_gen/train/G1001/faulty/m2_harvest_evaluator.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}harvest_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS evaluate + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE {{p}}if_harvest=>tt_member + RETURNING VALUE(rt_result) TYPE {{p}}if_harvest=>tt_result. + + PRIVATE SECTION. + TYPES tt_member_key TYPE HASHED TABLE OF {{p}}if_harvest=>ty_member + WITH UNIQUE KEY member_id. + TYPES tt_member_id TYPE SORTED TABLE OF {{p}}if_harvest=>ty_member_id + WITH UNIQUE KEY table_line. + TYPES tt_report_key TYPE SORTED TABLE OF {{p}}if_harvest=>ty_report + WITH NON-UNIQUE KEY variety member_id. + + METHODS countable_reports + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE tt_member_key + RETURNING VALUE(rt_report) TYPE tt_report_key. + METHODS is_countable + IMPORTING is_report TYPE {{p}}if_harvest=>ty_report + it_member TYPE tt_member_key + RETURNING VALUE(rv_count) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}harvest_evaluator IMPLEMENTATION. + METHOD evaluate. + DATA lt_member TYPE tt_member_key. + lt_member = it_member. + + DATA(lt_report) = countable_reports( it_report = it_report + it_member = lt_member ). + + DATA lt_result TYPE {{p}}if_harvest=>tt_result. + DATA lt_member_id TYPE tt_member_id. + LOOP AT lt_report ASSIGNING FIELD-SYMBOL() GROUP BY -variety. + DATA(lv_variety) = -variety. + DATA(lv_total) = 0. + CLEAR lt_member_id. + LOOP AT GROUP ASSIGNING FIELD-SYMBOL(). + lv_total = lv_total + -quantity_kg. + INSERT -member_id INTO TABLE lt_member_id. + ENDLOOP. + APPEND VALUE {{p}}if_harvest=>ty_result( + variety = lv_variety + total_kg = lv_total + member_count = lines( lt_member_id ) ) TO lt_result. + ENDLOOP. + + SORT lt_result BY total_kg DESCENDING variety ASCENDING. + rt_result = lt_result. + ENDMETHOD. + + METHOD countable_reports. + LOOP AT it_report INTO DATA(ls_report). + IF is_countable( is_report = ls_report + it_member = it_member ) = abap_true. + CONTINUE. + ENDIF. + INSERT ls_report INTO TABLE rt_report. + ENDLOOP. + ENDMETHOD. + + METHOD is_countable. + IF is_report-quantity_kg <= 0. + RETURN. + ENDIF. + rv_count = xsdbool( line_exists( it_member[ member_id = is_report-member_id ] ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/faulty/m3_harvest_evaluator.clas.abap b/tasks_gen/train/G1001/faulty/m3_harvest_evaluator.clas.abap new file mode 100644 index 0000000..15c4c48 --- /dev/null +++ b/tasks_gen/train/G1001/faulty/m3_harvest_evaluator.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}harvest_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS evaluate + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE {{p}}if_harvest=>tt_member + RETURNING VALUE(rt_result) TYPE {{p}}if_harvest=>tt_result. + + PRIVATE SECTION. + TYPES tt_member_key TYPE HASHED TABLE OF {{p}}if_harvest=>ty_member + WITH UNIQUE KEY member_id. + TYPES tt_member_id TYPE SORTED TABLE OF {{p}}if_harvest=>ty_member_id + WITH UNIQUE KEY table_line. + TYPES tt_report_key TYPE SORTED TABLE OF {{p}}if_harvest=>ty_report + WITH NON-UNIQUE KEY variety member_id. + + METHODS countable_reports + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE tt_member_key + RETURNING VALUE(rt_report) TYPE tt_report_key. + METHODS is_countable + IMPORTING is_report TYPE {{p}}if_harvest=>ty_report + it_member TYPE tt_member_key + RETURNING VALUE(rv_count) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}harvest_evaluator IMPLEMENTATION. + METHOD evaluate. + DATA lt_member TYPE tt_member_key. + lt_member = it_member. + + DATA(lt_report) = countable_reports( it_report = it_report + it_member = lt_member ). + + DATA lt_result TYPE {{p}}if_harvest=>tt_result. + DATA lt_member_id TYPE tt_member_id. + LOOP AT lt_report ASSIGNING FIELD-SYMBOL() GROUP BY -variety. + DATA(lv_variety) = -variety. + DATA(lv_total) = 0. + CLEAR lt_member_id. + LOOP AT GROUP ASSIGNING FIELD-SYMBOL(). + lv_total = lv_total + -quantity_kg. + INSERT -member_id INTO TABLE lt_member_id. + ENDLOOP. + APPEND VALUE {{p}}if_harvest=>ty_result( + variety = lv_variety + total_kg = lv_total + member_count = lines( lt_member_id ) ) TO lt_result. + ENDLOOP. + + SORT lt_result BY total_kg DESCENDING variety ASCENDING. + rt_result = lt_result. + ENDMETHOD. + + METHOD countable_reports. + LOOP AT it_report INTO DATA(ls_report). + IF is_countable( is_report = ls_report + it_member = it_member ) = abap_false. + CONTINUE. + ENDIF. + INSERT ls_report INTO TABLE rt_report. + ENDLOOP. + ENDMETHOD. + + METHOD is_countable. + IF is_report-quantity_kg > 0. + RETURN. + ENDIF. + rv_count = xsdbool( line_exists( it_member[ member_id = is_report-member_id ] ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/generation.json b/tasks_gen/train/G1001/generation.json new file mode 100644 index 0000000..153c3a4 --- /dev/null +++ b/tasks_gen/train/G1001/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1001", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "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/G1001/hidden/harvest_hidden.clas.abap b/tasks_gen/train/G1001/hidden/harvest_hidden.clas.abap new file mode 100644 index 0000000..db6996f --- /dev/null +++ b/tasks_gen/train/G1001/hidden/harvest_hidden.clas.abap @@ -0,0 +1,193 @@ +CLASS {{p}}harvest_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}harvest_evaluator. + + METHODS setup. + METHODS assert_rows + IMPORTING it_act TYPE {{p}}if_harvest=>tt_result + it_exp TYPE {{p}}if_harvest=>tt_result + iv_msg TYPE string. + + METHODS empty_input FOR TESTING. + METHODS unknown_member_ignored FOR TESTING. + METHODS zero_quantity_ignored FOR TESTING. + METHODS negative_quantity_ignored FOR TESTING. + METHODS same_member_counts_once FOR TESTING. + METHODS two_varieties_separated FOR TESTING. + METHODS sorted_by_total_desc FOR TESTING. + METHODS tie_broken_by_variety FOR TESTING. + METHODS all_reports_ignored FOR TESTING. + METHODS member_without_report FOR TESTING. +ENDCLASS. + + +CLASS {{p}}harvest_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}harvest_evaluator( ). + ENDMETHOD. + + METHOD assert_rows. + cl_abap_unit_assert=>assert_equals( exp = lines( it_exp ) + act = lines( it_act ) + msg = |{ iv_msg }: number of rows| ). + LOOP AT it_exp ASSIGNING FIELD-SYMBOL(). + READ TABLE it_act ASSIGNING FIELD-SYMBOL() INDEX sy-tabix. + IF sy-subrc <> 0. + RETURN. + ENDIF. + cl_abap_unit_assert=>assert_equals( exp = -variety + act = -variety + msg = |{ iv_msg }: variety| ). + cl_abap_unit_assert=>assert_equals( exp = -total_kg + act = -total_kg + msg = |{ iv_msg }: total_kg| ). + cl_abap_unit_assert=>assert_equals( exp = -member_count + act = -member_count + msg = |{ iv_msg }: member_count| ). + ENDLOOP. + ENDMETHOD. + + METHOD empty_input. + DATA lt_report TYPE {{p}}if_harvest=>tt_report. + DATA lt_member TYPE {{p}}if_harvest=>tt_member. + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD unknown_member_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 100 ) + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 50 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 100 member_count = 1 ) ) + iv_msg = 'unknown member' ). + ENDMETHOD. + + METHOD zero_quantity_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 30 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 30 member_count = 1 ) ) + iv_msg = 'zero quantity' ). + ENDMETHOD. + + METHOD negative_quantity_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = -5 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 20 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 20 member_count = 1 ) ) + iv_msg = 'negative quantity' ). + ENDMETHOD. + + METHOD same_member_counts_once. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 20 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 35 member_count = 2 ) ) + iv_msg = 'same member' ). + ENDMETHOD. + + METHOD two_varieties_separated. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = 7 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 20 member_count = 2 ) + ( variety = 'SPAE' total_kg = 7 member_count = 1 ) ) + iv_msg = 'two varieties' ). + ENDMETHOD. + + METHOD sorted_by_total_desc. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) + ( member_id = 'M00004' member_name = 'Hof Eiche' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'SPAE' quantity_kg = 30 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 20 ) + ( member_id = 'M00004' variety = 'SPAE' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'SPAE' total_kg = 35 member_count = 2 ) + ( variety = 'SILV' total_kg = 20 member_count = 1 ) + ( variety = 'RIES' total_kg = 10 member_count = 1 ) ) + iv_msg = 'sort by total' ). + ENDMETHOD. + + METHOD tie_broken_by_variety. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 10 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 10 member_count = 1 ) + ( variety = 'SILV' total_kg = 10 member_count = 1 ) + ( variety = 'SPAE' total_kg = 10 member_count = 1 ) ) + iv_msg = 'tie by variety' ). + ENDMETHOD. + + METHOD all_reports_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = -3 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD member_without_report. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 5 member_count = 1 ) ) + iv_msg = 'member without report' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/mutation.json b/tasks_gen/train/G1001/mutation.json new file mode 100644 index 0000000..18d61b3 --- /dev/null +++ b/tasks_gen/train/G1001/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1001", + "mutants": [ + { + "object": "{{P}}HARVEST_EVALUATOR", + "mutant": "line 42: + -> - (arith)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "MEMBER_WITHOUT_REPORT", + "NEGATIVE_QUANTITY_IGNORED", + "SAME_MEMBER_COUNTS_ONCE", + "SORTED_BY_TOTAL_DESC", + "TIE_BROKEN_BY_VARIETY", + "TWO_VARIETIES_SEPARATED", + "UNKNOWN_MEMBER_IGNORED", + "ZERO_QUANTITY_IGNORED" + ] + }, + { + "object": "{{P}}HARVEST_EVALUATOR", + "mutant": "line 57: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}HARVEST_EVALUATOR", + "mutant": "line 58: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "ALL_REPORTS_IGNORED", + "MEMBER_WITHOUT_REPORT", + "NEGATIVE_QUANTITY_IGNORED", + "SAME_MEMBER_COUNTS_ONCE", + "SORTED_BY_TOTAL_DESC", + "TIE_BROKEN_BY_VARIETY", + "TWO_VARIETIES_SEPARATED", + "UNKNOWN_MEMBER_IGNORED", + "ZERO_QUANTITY_IGNORED" + ] + }, + { + "object": "{{P}}HARVEST_EVALUATOR", + "mutant": "line 66: <= -> > (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "ALL_REPORTS_IGNORED", + "MEMBER_WITHOUT_REPORT", + "NEGATIVE_QUANTITY_IGNORED", + "SAME_MEMBER_COUNTS_ONCE", + "SORTED_BY_TOTAL_DESC", + "TIE_BROKEN_BY_VARIETY", + "TWO_VARIETIES_SEPARATED", + "UNKNOWN_MEMBER_IGNORED", + "ZERO_QUANTITY_IGNORED" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1001/reference/harvest_evaluator.clas.abap b/tasks_gen/train/G1001/reference/harvest_evaluator.clas.abap new file mode 100644 index 0000000..edc0251 --- /dev/null +++ b/tasks_gen/train/G1001/reference/harvest_evaluator.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}harvest_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS evaluate + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE {{p}}if_harvest=>tt_member + RETURNING VALUE(rt_result) TYPE {{p}}if_harvest=>tt_result. + + PRIVATE SECTION. + TYPES tt_member_key TYPE HASHED TABLE OF {{p}}if_harvest=>ty_member + WITH UNIQUE KEY member_id. + TYPES tt_member_id TYPE SORTED TABLE OF {{p}}if_harvest=>ty_member_id + WITH UNIQUE KEY table_line. + TYPES tt_report_key TYPE SORTED TABLE OF {{p}}if_harvest=>ty_report + WITH NON-UNIQUE KEY variety member_id. + + METHODS countable_reports + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE tt_member_key + RETURNING VALUE(rt_report) TYPE tt_report_key. + METHODS is_countable + IMPORTING is_report TYPE {{p}}if_harvest=>ty_report + it_member TYPE tt_member_key + RETURNING VALUE(rv_count) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}harvest_evaluator IMPLEMENTATION. + METHOD evaluate. + DATA lt_member TYPE tt_member_key. + lt_member = it_member. + + DATA(lt_report) = countable_reports( it_report = it_report + it_member = lt_member ). + + DATA lt_result TYPE {{p}}if_harvest=>tt_result. + DATA lt_member_id TYPE tt_member_id. + LOOP AT lt_report ASSIGNING FIELD-SYMBOL() GROUP BY -variety. + DATA(lv_variety) = -variety. + DATA(lv_total) = 0. + CLEAR lt_member_id. + LOOP AT GROUP ASSIGNING FIELD-SYMBOL(). + lv_total = lv_total + -quantity_kg. + INSERT -member_id INTO TABLE lt_member_id. + ENDLOOP. + APPEND VALUE {{p}}if_harvest=>ty_result( + variety = lv_variety + total_kg = lv_total + member_count = lines( lt_member_id ) ) TO lt_result. + ENDLOOP. + + SORT lt_result BY total_kg DESCENDING variety ASCENDING. + rt_result = lt_result. + ENDMETHOD. + + METHOD countable_reports. + LOOP AT it_report INTO DATA(ls_report). + IF is_countable( is_report = ls_report + it_member = it_member ) = abap_false. + CONTINUE. + ENDIF. + INSERT ls_report INTO TABLE rt_report. + ENDLOOP. + ENDMETHOD. + + METHOD is_countable. + IF is_report-quantity_kg <= 0. + RETURN. + ENDIF. + rv_count = xsdbool( line_exists( it_member[ member_id = is_report-member_id ] ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/reference/harvest_evaluator.testclasses.abap b/tasks_gen/train/G1001/reference/harvest_evaluator.testclasses.abap new file mode 100644 index 0000000..86dbb70 --- /dev/null +++ b/tasks_gen/train/G1001/reference/harvest_evaluator.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_harvest_evaluator DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}harvest_evaluator. + METHODS setup. + METHODS sums_quantities FOR TESTING. + METHODS counts_members_once FOR TESTING. + METHODS ignores_unknown_member FOR TESTING. + METHODS ignores_zero_quantity FOR TESTING. + METHODS sorts_by_total FOR TESTING. +ENDCLASS. + + +CLASS ltc_harvest_evaluator IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}harvest_evaluator( ). + ENDMETHOD. + + METHOD sums_quantities. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 15 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 25 act = lt_result[ 1 ]-total_kg ). + ENDMETHOD. + + METHOD counts_members_once. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_result[ 1 ]-total_kg ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD ignores_unknown_member. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 99 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_kg ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD ignores_zero_quantity. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 40 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 40 act = lt_result[ 1 ]-total_kg ). + ENDMETHOD. + + METHOD sorts_by_total. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) + ( member_id = 'M00002' variety = 'SPAE' quantity_kg = 30 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 20 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 'SPAE' act = lt_result[ 1 ]-variety ). + cl_abap_unit_assert=>assert_equals( exp = 'SILV' act = lt_result[ 2 ]-variety ). + cl_abap_unit_assert=>assert_equals( exp = 'RIES' act = lt_result[ 3 ]-variety ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1001/seed/if_harvest.intf.abap b/tasks_gen/train/G1001/seed/if_harvest.intf.abap new file mode 100644 index 0000000..103a82c --- /dev/null +++ b/tasks_gen/train/G1001/seed/if_harvest.intf.abap @@ -0,0 +1,26 @@ +INTERFACE {{p}}if_harvest PUBLIC. + TYPES ty_member_id TYPE c LENGTH 6. + TYPES ty_variety TYPE c LENGTH 4. + TYPES ty_name TYPE c LENGTH 30. + TYPES ty_quantity TYPE i. + + TYPES: BEGIN OF ty_member, + member_id TYPE ty_member_id, + member_name TYPE ty_name, + END OF ty_member. + TYPES tt_member TYPE STANDARD TABLE OF ty_member WITH EMPTY KEY. + + TYPES: BEGIN OF ty_report, + member_id TYPE ty_member_id, + variety TYPE ty_variety, + quantity_kg TYPE ty_quantity, + END OF ty_report. + TYPES tt_report TYPE STANDARD TABLE OF ty_report WITH EMPTY KEY. + + TYPES: BEGIN OF ty_result, + variety TYPE ty_variety, + total_kg TYPE ty_quantity, + member_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY. +ENDINTERFACE. diff --git a/tasks_gen/train/G1001/spec.md b/tasks_gen/train/G1001/spec.md new file mode 100644 index 0000000..844dfa5 --- /dev/null +++ b/tasks_gen/train/G1001/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A vineyard cooperative collects the harvest reports of its members. The +cooperative needs one evaluation of the harvest for each grape variety. A +planning program calls the evaluation before it plans the transport of the +grapes to the winery. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_HARVEST exists in package $TMP. It is active. +- The interface contains the data types for the harvest reports, for the member + master data, and for the result. Read the interface before you start to work. +- A harvest report has a member, a grape variety, and a quantity in kilograms. +- The member master data has a member ID and a member name. + +# 4. Contract +- Create the class {{P}}HARVEST_EVALUATOR in package $TMP. +- The class is public and final. It has a public constructor without + parameters. +- The class has one public method: + EVALUATE + IMPORTING it_report TYPE {{P}}IF_HARVEST=>TT_REPORT + it_member TYPE {{P}}IF_HARVEST=>TT_MEMBER + RETURNING VALUE(rt_result) TYPE {{P}}IF_HARVEST=>TT_RESULT. +- Do not add other public methods. + +# 5. Business rules +A harvest report is countable when rule 1 and rule 2 are true. +1. The member_id of the report occurs in it_member. A member_id occurs at most + one time in it_member. +2. The quantity_kg of the report is greater than 0. +3. Group the countable reports by variety. Create one result row for each + variety that has at least one countable report. +4. The total_kg of a result row is the sum of the quantity_kg of all countable + reports of the variety. +5. The member_count of a result row is the number of different member_id + values in the countable reports of the variety. +6. Sort the result rows by total_kg in descending order. When two rows have the + same total_kg, sort these rows by variety in ascending order. +7. When no report is countable, return an empty table. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_HARVEST. + +# 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/G1001/task.json b/tasks_gen/train/G1001/task.json new file mode 100644 index 0000000..7534be8 --- /dev/null +++ b/tasks_gen/train/G1001/task.json @@ -0,0 +1,53 @@ +{ + "id": "G1001", + "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_HARVEST", + "file": "seed/if_harvest.intf.abap", + "description": "Data types for harvest reports, member master data, and the evaluation result" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_EVALUATOR", + "description": "Evaluates the harvest reports per grape variety" + } + ], + "out_of_scope": [ + "{{P}}IF_HARVEST" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_HIDDEN", + "file": "hidden/harvest_hidden.clas.abap", + "description": "Hidden tests for the harvest evaluation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_EVALUATOR", + "file": "reference/harvest_evaluator.clas.abap", + "description": "Harvest evaluator", + "testclasses_file": "reference/harvest_evaluator.testclasses.abap" + } + ], + "craft_checks": [ + "table_kind", + "key_kind", + "access_path", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1002/faulty/m0_glasses_pricer.clas.abap b/tasks_gen/train/G1002/faulty/m0_glasses_pricer.clas.abap new file mode 100644 index 0000000..4a6a15d --- /dev/null +++ b/tasks_gen/train/G1002/faulty/m0_glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens / iv_qty. + rv_total = rv_total + iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/faulty/m1_glasses_pricer.clas.abap b/tasks_gen/train/G1002/faulty/m1_glasses_pricer.clas.abap new file mode 100644 index 0000000..a4f8fdd --- /dev/null +++ b/tasks_gen/train/G1002/faulty/m1_glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total - iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/faulty/m2_glasses_pricer.clas.abap b/tasks_gen/train/G1002/faulty/m2_glasses_pricer.clas.abap new file mode 100644 index 0000000..c5eee94 --- /dev/null +++ b/tasks_gen/train/G1002/faulty/m2_glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total + iv_coat / iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/faulty/m3_glasses_pricer.clas.abap b/tasks_gen/train/G1002/faulty/m3_glasses_pricer.clas.abap new file mode 100644 index 0000000..e3459de --- /dev/null +++ b/tasks_gen/train/G1002/faulty/m3_glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total + iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty < c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/faulty/m4_glasses_pricer.clas.abap b/tasks_gen/train/G1002/faulty/m4_glasses_pricer.clas.abap new file mode 100644 index 0000000..a582309 --- /dev/null +++ b/tasks_gen/train/G1002/faulty/m4_glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total + iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total + iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/generation.json b/tasks_gen/train/G1002/generation.json new file mode 100644 index 0000000..e8e45c1 --- /dev/null +++ b/tasks_gen/train/G1002/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1002", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "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/G1002/hidden/glasses_hidden.clas.abap b/tasks_gen/train/G1002/hidden/glasses_hidden.clas.abap new file mode 100644 index 0000000..757c44e --- /dev/null +++ b/tasks_gen/train/G1002/hidden/glasses_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}glasses_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CONSTANTS c_frame TYPE {{p}}glasses_pricer=>ty_amount VALUE '100.00'. + DATA mo_cut TYPE REF TO {{p}}glasses_pricer. + + METHODS setup. + METHODS zero_qty_is_free FOR TESTING. + METHODS negative_qty_is_free FOR TESTING. + METHODS single_vision_price FOR TESTING. + METHODS bifocal_with_coating FOR TESTING. + METHODS progressive_blue_filter FOR TESTING. + METHODS unknown_lens_defaults FOR TESTING. + METHODS unknown_coat_is_free FOR TESTING. + METHODS discount_from_three FOR TESTING. + METHODS no_discount_for_two FOR TESTING. + METHODS qty_capped_at_hundred FOR TESTING. + METHODS discount_on_total FOR TESTING. +ENDCLASS. + + +CLASS {{p}}glasses_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}glasses_pricer( ). + ENDMETHOD. + + METHOD zero_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'A' iv_qty = 0 ) ). + ENDMETHOD. + + METHOD negative_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'P' iv_coat = 'B' iv_qty = -2 ) ). + ENDMETHOD. + + METHOD single_vision_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD bifocal_with_coating. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '430.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'B' iv_coat = 'A' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD progressive_blue_filter. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '235.00' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '50.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_lens_defaults. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'Z' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_coat_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'X' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD discount_from_three. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '378.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 3 ) ). + ENDMETHOD. + + METHOD no_discount_for_two. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '280.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD qty_capped_at_hundred. + DATA(lv_capped) = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 100 ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '12600.00' ) + act = lv_capped ). + cl_abap_unit_assert=>assert_equals( + exp = lv_capped + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 150 ) ). + ENDMETHOD. + + METHOD discount_on_total. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '499.50' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 3 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/mutation.json b/tasks_gen/train/G1002/mutation.json new file mode 100644 index 0000000..9531bc3 --- /dev/null +++ b/tasks_gen/train/G1002/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1002", + "mutants": [ + { + "object": "{{P}}GLASSES_PRICER", + "mutant": "line 99: * -> / (arith)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "BIFOCAL_WITH_COATING", + "DISCOUNT_FROM_THREE", + "DISCOUNT_ON_TOTAL", + "NO_DISCOUNT_FOR_TWO", + "QTY_CAPPED_AT_HUNDRED" + ] + }, + { + "object": "{{P}}GLASSES_PRICER", + "mutant": "line 100: + -> - (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "BIFOCAL_WITH_COATING", + "DISCOUNT_ON_TOTAL", + "PROGRESSIVE_BLUE_FILTER" + ] + }, + { + "object": "{{P}}GLASSES_PRICER", + "mutant": "line 100: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BIFOCAL_WITH_COATING", + "DISCOUNT_ON_TOTAL" + ] + }, + { + "object": "{{P}}GLASSES_PRICER", + "mutant": "line 105: >= -> < (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BIFOCAL_WITH_COATING", + "DISCOUNT_FROM_THREE", + "DISCOUNT_ON_TOTAL", + "NO_DISCOUNT_FOR_TWO", + "PROGRESSIVE_BLUE_FILTER", + "QTY_CAPPED_AT_HUNDRED", + "SINGLE_VISION_PRICE", + "UNKNOWN_COAT_IS_FREE", + "UNKNOWN_LENS_DEFAULTS" + ] + }, + { + "object": "{{P}}GLASSES_PRICER", + "mutant": "line 106: - -> + (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "DISCOUNT_FROM_THREE", + "DISCOUNT_ON_TOTAL", + "QTY_CAPPED_AT_HUNDRED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1002/reference/glasses_pricer.clas.abap b/tasks_gen/train/G1002/reference/glasses_pricer.clas.abap new file mode 100644 index 0000000..84d5a08 --- /dev/null +++ b/tasks_gen/train/G1002/reference/glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total + iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/reference/glasses_pricer.testclasses.abap b/tasks_gen/train/G1002/reference/glasses_pricer.testclasses.abap new file mode 100644 index 0000000..ecffa90 --- /dev/null +++ b/tasks_gen/train/G1002/reference/glasses_pricer.testclasses.abap @@ -0,0 +1,83 @@ +CLASS ltc_glasses_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS c_frame TYPE {{p}}glasses_pricer=>ty_amount VALUE '100.00'. + DATA mo_cut TYPE REF TO {{p}}glasses_pricer. + + METHODS setup. + METHODS single_vision FOR TESTING. + METHODS bifocal_with_coating FOR TESTING. + METHODS progressive_with_blue FOR TESTING. + METHODS unknown_lens_defaults FOR TESTING. + METHODS unknown_coat_is_free FOR TESTING. + METHODS discount_from_three FOR TESTING. + METHODS no_discount_for_two FOR TESTING. + METHODS zero_qty_is_free FOR TESTING. + METHODS qty_capped_at_hundred FOR TESTING. +ENDCLASS. + + +CLASS ltc_glasses_pricer IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW #( ). + ENDMETHOD. + + METHOD single_vision. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD bifocal_with_coating. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '430.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'B' iv_coat = 'A' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD progressive_with_blue. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '235.00' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '50.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_lens_defaults. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'Z' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_coat_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'X' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD discount_from_three. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '378.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 3 ) ). + ENDMETHOD. + + METHOD no_discount_for_two. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '280.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD zero_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'A' iv_qty = 0 ) ). + ENDMETHOD. + + METHOD qty_capped_at_hundred. + DATA(lv_capped) = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 100 ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '12600.00' ) + act = lv_capped ). + cl_abap_unit_assert=>assert_equals( + exp = lv_capped + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 150 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1002/seed/glasses_pricer.clas.abap b/tasks_gen/train/G1002/seed/glasses_pricer.clas.abap new file mode 100644 index 0000000..6c2f86c --- /dev/null +++ b/tasks_gen/train/G1002/seed/glasses_pricer.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2 . + TYPES ty_lens TYPE c LENGTH 1 . + TYPES ty_coat TYPE c LENGTH 1 . + METHODS price + IMPORTING + iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING + VALUE(rv_price) TYPE ty_amount . + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + + DATA lv_qty TYPE i . + DATA lv_frame TYPE ty_amount . + DATA lv_lens TYPE ty_amount . + DATA lv_coat TYPE ty_amount . + DATA lv_total TYPE ty_amount . + + lv_qty = iv_qty . + IF lv_qty > 100 . + lv_qty = 100 . + ENDIF . + + IF lv_qty < 1 . + rv_price = 0 . + RETURN . + ENDIF . + + lv_frame = iv_frame * lv_qty . + + IF iv_lens = 'S' . + lv_lens = 40 . + ELSEIF iv_lens = 'B' . + lv_lens = 90 . + ELSEIF iv_lens = 'P' . + lv_lens = 150 . + ELSE . + lv_lens = 40 . + ENDIF . + lv_lens = lv_lens * lv_qty . + + CASE iv_coat . + WHEN 'A' . + lv_coat = 25 . + WHEN 'B' . + lv_coat = 35 . + WHEN OTHERS . + lv_coat = 0 . + ENDCASE . + lv_coat = lv_coat * lv_qty . + + lv_total = lv_frame + lv_lens + lv_coat . + + IF lv_qty >= 3 . + lv_total = lv_total - lv_total * 10 / 100 . + ENDIF . + + rv_price = lv_total . + + ENDMETHOD . +ENDCLASS. diff --git a/tasks_gen/train/G1002/seed/optic_quote_rpt.prog.abap b/tasks_gen/train/G1002/seed/optic_quote_rpt.prog.abap new file mode 100644 index 0000000..d3d4786 --- /dev/null +++ b/tasks_gen/train/G1002/seed/optic_quote_rpt.prog.abap @@ -0,0 +1,12 @@ +REPORT {{p}}optic_quote_rpt. + +DATA lo_pricer TYPE REF TO {{p}}glasses_pricer. +DATA lv_price TYPE {{p}}glasses_pricer=>ty_amount. + +lo_pricer = NEW #( ). +lv_price = lo_pricer->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '120.00' ) + iv_lens = 'P' + iv_coat = 'A' + iv_qty = 2 ). + +WRITE: / 'Quotation:', lv_price. diff --git a/tasks_gen/train/G1002/spec.md b/tasks_gen/train/G1002/spec.md new file mode 100644 index 0000000..6802c4d --- /dev/null +++ b/tasks_gen/train/G1002/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +The optician shop uses the class {{P}}GLASSES_PRICER to calculate the price of a +glasses order. A sales program calls the class for each quotation. The class is +legacy code. It is hard to read and hard to test. Refactor the class. The +behavior must stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}GLASSES_PRICER exists in package $TMP. It is active. +- The class is public and final. It has a public constructor without parameters. +- The public interface of the class is: + - the type TY_AMOUNT (packed number, length 9, 2 decimals), + - the type TY_LENS (character, length 1), + - the type TY_COAT (character, length 1), + - the method PRICE with the importing parameters IV_FRAME (TY_AMOUNT), + IV_LENS (TY_LENS), IV_COAT (TY_COAT), IV_QTY (TYPE i) and the returning + parameter RV_PRICE (TY_AMOUNT). +- The program {{P}}OPTIC_QUOTE_RPT calls the class. +- The legacy implementation is the specification of the behavior. + +# 4. Contract +- Refactor the class {{P}}GLASSES_PRICER in package $TMP. The name stays the same. +- The class stays public and final and keeps its public constructor without + parameters. +- Keep the public types TY_AMOUNT, TY_LENS and TY_COAT. +- Keep the method PRICE with the same parameter names, the same parameter types + and the same sequence. +- Do not add public methods or public attributes. +- Do not create other objects. + +# 5. Business rules +- The behavior of the class must not change. For every input, PRICE must return + the same value as the legacy method. +- The legacy code holds all rules, including its limits and its default values. +- Do not add a rule. Do not remove a rule. Do not correct a rule. +- PRICE returns a price in the currency of the order. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. One method does one thing. Method length below + 40 statements. No deep nesting. No magic numbers: use constants. No duplicated + code. No dead code. No global variables. No comment that restates the code. +- Out of scope: do not change the program {{P}}OPTIC_QUOTE_RPT. Do not change + the public interface of the class. + +# 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/G1002/task.json b/tasks_gen/train/G1002/task.json new file mode 100644 index 0000000..c88d588 --- /dev/null +++ b/tasks_gen/train/G1002/task.json @@ -0,0 +1,57 @@ +{ + "id": "G1002", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER", + "file": "seed/glasses_pricer.clas.abap", + "description": "Legacy price calculator for glasses orders" + }, + { + "type": "PROG", + "name": "{{P}}OPTIC_QUOTE_RPT", + "file": "seed/optic_quote_rpt.prog.abap", + "description": "Report that calls the legacy class" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER" + } + ], + "out_of_scope": [ + "{{P}}OPTIC_QUOTE_RPT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_HIDDEN", + "file": "hidden/glasses_hidden.clas.abap", + "description": "Hidden behavior tests for the refactored class" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER", + "file": "reference/glasses_pricer.clas.abap", + "description": "Refactored glasses price calculator", + "testclasses_file": "reference/glasses_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth", + "magic_numbers" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1003/faulty/m0_drone_checker.clas.abap b/tasks_gen/train/G1003/faulty/m0_drone_checker.clas.abap new file mode 100644 index 0000000..a1302ae --- /dev/null +++ b/tasks_gen/train/G1003/faulty/m0_drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 AND iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance > c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/faulty/m1_drone_checker.clas.abap b/tasks_gen/train/G1003/faulty/m1_drone_checker.clas.abap new file mode 100644 index 0000000..f4adeeb --- /dev/null +++ b/tasks_gen/train/G1003/faulty/m1_drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 OR iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance <= c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/faulty/m2_drone_checker.clas.abap b/tasks_gen/train/G1003/faulty/m2_drone_checker.clas.abap new file mode 100644 index 0000000..c58e96b --- /dev/null +++ b/tasks_gen/train/G1003/faulty/m2_drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 OR iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance > c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone <> abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/faulty/m3_drone_checker.clas.abap b/tasks_gen/train/G1003/faulty/m3_drone_checker.clas.abap new file mode 100644 index 0000000..8a99614 --- /dev/null +++ b/tasks_gen/train/G1003/faulty/m3_drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 OR iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance > c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_true. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/generation.json b/tasks_gen/train/G1003/generation.json new file mode 100644 index 0000000..cac9d67 --- /dev/null +++ b/tasks_gen/train/G1003/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1003", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 70.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/G1003/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1003/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..3429d97 --- /dev/null +++ b/tasks_gen/train/G1003/hidden/t21_hidden.clas.abap @@ -0,0 +1,172 @@ +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_drone_check. + DATA mo_drone TYPE REF TO {{p}}drone_checker. + + METHODS setup. + + METHODS ok_flight FOR TESTING. + METHODS payload_zero FOR TESTING. + METHODS payload_too_heavy FOR TESTING. + METHODS distance_zero FOR TESTING. + METHODS distance_too_far FOR TESTING. + METHODS wind_too_strong FOR TESTING. + METHODS battery_too_low FOR TESTING. + METHODS battery_exact_ok FOR TESTING. + METHODS outside_zone FOR TESTING. + METHODS no_daylight FOR TESTING. + METHODS required_battery_rounds_up FOR TESTING. + METHODS order_payload_first FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD setup. + mo_drone = NEW {{p}}drone_checker( ). + mo_cut = mo_drone. + ENDMETHOD. + + METHOD ok_flight. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '5.00' + iv_distance = '20.00' + iv_wind_speed = '12.00' + iv_battery = 50 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD payload_zero. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '0.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD payload_too_heavy. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '5.01' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD distance_zero. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '0.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD distance_too_far. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '20.01' + iv_wind_speed = '5.00' + iv_battery = 60 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD wind_too_strong. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-wind + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '12.01' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD battery_too_low. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 29 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '7.50' + iv_wind_speed = '5.00' + iv_battery = 25 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD battery_exact_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD outside_zone. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-zone + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_false + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD no_daylight. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-light + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_false ) ). + ENDMETHOD. + + METHOD required_battery_rounds_up. + cl_abap_unit_assert=>assert_equals( exp = 26 act = mo_drone->required_battery( '7.50' ) ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = mo_drone->required_battery( '20.00' ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_drone->required_battery( '0.00' ) ). + ENDMETHOD. + + METHOD order_payload_first. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '6.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/mutation.json b/tasks_gen/train/G1003/mutation.json new file mode 100644 index 0000000..e4621f8 --- /dev/null +++ b/tasks_gen/train/G1003/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1003", + "mutants": [ + { + "object": "{{P}}DRONE_CHECKER", + "mutant": "line 21: OR -> AND (logic)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "ORDER_PAYLOAD_FIRST", + "PAYLOAD_TOO_HEAVY", + "PAYLOAD_ZERO" + ] + }, + { + "object": "{{P}}DRONE_CHECKER", + "mutant": "line 23: > -> <= (rel)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "BATTERY_EXACT_OK", + "BATTERY_TOO_LOW", + "DISTANCE_TOO_FAR", + "NO_DAYLIGHT", + "OK_FLIGHT", + "OUTSIDE_ZONE", + "WIND_TOO_STRONG" + ] + }, + { + "object": "{{P}}DRONE_CHECKER", + "mutant": "line 29: = -> <> (eq)", + "status": "killed", + "hidden": "8/12", + "failed_tests": [ + "BATTERY_EXACT_OK", + "NO_DAYLIGHT", + "OK_FLIGHT", + "OUTSIDE_ZONE" + ] + }, + { + "object": "{{P}}DRONE_CHECKER", + "mutant": "line 31: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "BATTERY_EXACT_OK", + "NO_DAYLIGHT", + "OK_FLIGHT" + ] + }, + { + "object": "{{P}}DRONE_CHECKER", + "mutant": "line 45: + -> - (arith)", + "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/G1003/reference/drone_checker.clas.abap b/tasks_gen/train/G1003/reference/drone_checker.clas.abap new file mode 100644 index 0000000..9028885 --- /dev/null +++ b/tasks_gen/train/G1003/reference/drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 OR iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance > c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/reference/drone_checker.testclasses.abap b/tasks_gen/train/G1003/reference/drone_checker.testclasses.abap new file mode 100644 index 0000000..c7004d3 --- /dev/null +++ b/tasks_gen/train/G1003/reference/drone_checker.testclasses.abap @@ -0,0 +1,110 @@ +CLASS ltc_drone_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_drone_check. + DATA mo_drone TYPE REF TO {{p}}drone_checker. + + METHODS setup. + METHODS valid_request_is_ok FOR TESTING. + METHODS heavy_payload_is_rejected FOR TESTING. + METHODS strong_wind_is_rejected FOR TESTING. + METHODS low_battery_is_rejected FOR TESTING. + METHODS required_battery_is_rounded_up FOR TESTING. + METHODS zone_and_light_are_checked FOR TESTING. + METHODS reason_order_is_kept FOR TESTING. +ENDCLASS. + + +CLASS ltc_drone_checker IMPLEMENTATION. + METHOD setup. + mo_drone = NEW {{p}}drone_checker( ). + mo_cut = mo_drone. + ENDMETHOD. + + METHOD valid_request_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD heavy_payload_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '5.01' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD strong_wind_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-wind + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '12.01' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD low_battery_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 29 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD required_battery_is_rounded_up. + cl_abap_unit_assert=>assert_equals( exp = 26 act = mo_drone->required_battery( '7.50' ) ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = mo_drone->required_battery( '10.00' ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_drone->required_battery( '0.00' ) ). + ENDMETHOD. + + METHOD zone_and_light_are_checked. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-zone + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_false + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-light + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_false ) ). + ENDMETHOD. + + METHOD reason_order_is_kept. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '6.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1003/seed/if_drone_check.intf.abap b/tasks_gen/train/G1003/seed/if_drone_check.intf.abap new file mode 100644 index 0000000..c67d0b8 --- /dev/null +++ b/tasks_gen/train/G1003/seed/if_drone_check.intf.abap @@ -0,0 +1,25 @@ +INTERFACE {{p}}if_drone_check PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_weight TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_distance TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_speed TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_percent TYPE i. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + payload TYPE ty_reason VALUE 'PAYLOAD', + distance TYPE ty_reason VALUE 'DISTANCE', + wind TYPE ty_reason VALUE 'WIND', + battery TYPE ty_reason VALUE 'BATTERY', + zone TYPE ty_reason VALUE 'ZONE', + light TYPE ty_reason VALUE 'LIGHT', + END OF c_reason. + METHODS check + IMPORTING iv_payload TYPE ty_weight + iv_distance TYPE ty_distance + iv_wind_speed TYPE ty_speed + iv_battery TYPE ty_percent + iv_in_zone TYPE abap_bool + iv_daylight TYPE abap_bool + RETURNING VALUE(rv_reason) TYPE ty_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1003/spec.md b/tasks_gen/train/G1003/spec.md new file mode 100644 index 0000000..21e21c6 --- /dev/null +++ b/tasks_gen/train/G1003/spec.md @@ -0,0 +1,65 @@ +# 1. Goal +A drone delivery operator checks a delivery request before it releases a drone. +The dispatch program calls one check for each request. The program needs one +reason code for each request and the battery charge that the flight needs. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_DRONE_CHECK exists in package $TMP. It is active. +- The interface has the types TY_REASON (character, length 10), TY_WEIGHT + (packed number, length 8, 2 decimals), TY_DISTANCE (packed number, length 8, + 2 decimals), TY_SPEED (packed number, length 8, 2 decimals) and TY_PERCENT + (integer). +- The interface has the constant structure C_REASON with the components OK, + PAYLOAD, DISTANCE, WIND, BATTERY, ZONE and LIGHT. +- The interface has one method: + CHECK + IMPORTING iv_payload TYPE ty_weight + iv_distance TYPE ty_distance + iv_wind_speed TYPE ty_speed + iv_battery TYPE ty_percent + iv_in_zone TYPE abap_bool + iv_daylight TYPE abap_bool + RETURNING VALUE(rv_reason) TYPE ty_reason. + +# 4. Contract +- Create the class {{P}}DRONE_CHECKER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_DRONE_CHECK. +- The class has the public method REQUIRED_BATTERY: + IMPORTING iv_distance TYPE {{P}}IF_DRONE_CHECK=>TY_DISTANCE + RETURNING VALUE(rv_percent) TYPE {{P}}IF_DRONE_CHECK=>TY_PERCENT. +- Do not add other public methods. + +# 5. Business rules +The payload is in kilograms, the distance in kilometres, the wind speed in +metres per second, and the battery charge in percent. +The check applies the rules in this sequence and returns the reason of the +first rule that fails. +1. If the payload is less than or equal to zero, or greater than 5.00, return + PAYLOAD. +2. If the distance is less than or equal to zero, or greater than 20.00, return + DISTANCE. +3. If the wind speed is greater than 12.00, return WIND. +4. If the battery charge is less than the charge that the flight needs, return + BATTERY. +5. If iv_in_zone is abap_false, return ZONE. +6. If iv_daylight is abap_false, return LIGHT. +7. Else return OK. +The charge that a flight needs is the result of REQUIRED_BATTERY: +- If the distance is less than or equal to zero, the result is 0. +- Else the result is 2 percent for each started kilometre plus a reserve of + 10 percent. A started kilometre counts as a full kilometre. + +# 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_DRONE_CHECK. + +# 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/G1003/task.json b/tasks_gen/train/G1003/task.json new file mode 100644 index 0000000..d37bef9 --- /dev/null +++ b/tasks_gen/train/G1003/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1003", + "category": "A", + "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_DRONE_CHECK", + "file": "seed/if_drone_check.intf.abap", + "description": "Drone flight check contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}DRONE_CHECKER", + "implements": "{{P}}IF_DRONE_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_DRONE_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}DRONE_CHECKER", + "file": "reference/drone_checker.clas.abap", + "description": "Drone flight checker", + "testclasses_file": "reference/drone_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1004/faulty/m0_balloon_checker.clas.abap b/tasks_gen/train/G1004/faulty/m0_balloon_checker.clas.abap new file mode 100644 index 0000000..d01d13b --- /dev/null +++ b/tasks_gen/train/G1004/faulty/m0_balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers <= c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/faulty/m1_balloon_checker.clas.abap b/tasks_gen/train/G1004/faulty/m1_balloon_checker.clas.abap new file mode 100644 index 0000000..cb3224c --- /dev/null +++ b/tasks_gen/train/G1004/faulty/m1_balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg <= c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/faulty/m2_balloon_checker.clas.abap b/tasks_gen/train/G1004/faulty/m2_balloon_checker.clas.abap new file mode 100644 index 0000000..9cf1471 --- /dev/null +++ b/tasks_gen/train/G1004/faulty/m2_balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh <= c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/faulty/m3_balloon_checker.clas.abap b/tasks_gen/train/G1004/faulty/m3_balloon_checker.clas.abap new file mode 100644 index 0000000..dfd8f75 --- /dev/null +++ b/tasks_gen/train/G1004/faulty/m3_balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m >= c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/faulty/m4_balloon_checker.clas.abap b/tasks_gen/train/G1004/faulty/m4_balloon_checker.clas.abap new file mode 100644 index 0000000..83e8c4e --- /dev/null +++ b/tasks_gen/train/G1004/faulty/m4_balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg + iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/generation.json b/tasks_gen/train/G1004/generation.json new file mode 100644 index 0000000..c308e16 --- /dev/null +++ b/tasks_gen/train/G1004/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1004", + "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/G1004/hidden/balloon_hidden.clas.abap b/tasks_gen/train/G1004/hidden/balloon_hidden.clas.abap new file mode 100644 index 0000000..c02f574 --- /dev/null +++ b/tasks_gen/train/G1004/hidden/balloon_hidden.clas.abap @@ -0,0 +1,169 @@ +CLASS {{p}}balloon_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}balloon_checker. + + METHODS setup. + METHODS free_payload + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i. + + METHODS valid_flight_is_released FOR TESTING. + METHODS crew_above_limit FOR TESTING. + METHODS weight_above_limit FOR TESTING. + METHODS wind_above_limit FOR TESTING. + METHODS cloud_base_below_limit FOR TESTING. + METHODS crew_is_checked_first FOR TESTING. + METHODS weight_is_checked_second FOR TESTING. + METHODS wind_is_checked_third FOR TESTING. + METHODS limits_are_accepted FOR TESTING. + METHODS free_payload_is_returned FOR TESTING. +ENDCLASS. + + +CLASS {{p}}balloon_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}balloon_checker( ). + ENDMETHOD. + + METHOD free_payload. + TRY. + rv_free_kg = mo_cut->check_flight( iv_passengers = iv_passengers + iv_weight_kg = iv_weight_kg + iv_wind_kmh = iv_wind_kmh + iv_cloud_base_m = iv_cloud_base_m ). + CATCH {{p}}cx_balloon_flight. + rv_free_kg = -1. + ENDTRY. + ENDMETHOD. + + METHOD valid_flight_is_released. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD crew_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. + + METHOD weight_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_weight INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Weight 700 kg exceeds the limit 640 kg' + act = lv_text ). + ENDMETHOD. + + METHOD wind_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_wind INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Wind 25 km/h exceeds the limit 18 km/h' + act = lv_text ). + ENDMETHOD. + + METHOD cloud_base_below_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 200 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_cloud INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Cloud base 200 m is below the limit 300 m' + act = lv_text ). + ENDMETHOD. + + METHOD crew_is_checked_first. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. + + METHOD weight_is_checked_second. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_weight INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Weight 700 kg exceeds the limit 640 kg' + act = lv_text ). + ENDMETHOD. + + METHOD wind_is_checked_third. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_wind INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Wind 25 km/h exceeds the limit 18 km/h' + act = lv_text ). + ENDMETHOD. + + METHOD limits_are_accepted. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = free_payload( iv_passengers = 8 iv_weight_kg = 640 + iv_wind_kmh = 18 iv_cloud_base_m = 300 ) ). + ENDMETHOD. + + METHOD free_payload_is_returned. + cl_abap_unit_assert=>assert_equals( + exp = 40 + act = free_payload( iv_passengers = 2 iv_weight_kg = 600 + iv_wind_kmh = 12 iv_cloud_base_m = 1200 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/mutation.json b/tasks_gen/train/G1004/mutation.json new file mode 100644 index 0000000..b78b72d --- /dev/null +++ b/tasks_gen/train/G1004/mutation.json @@ -0,0 +1,80 @@ +{ + "task": "G1004", + "mutants": [ + { + "object": "{{P}}BALLOON_CHECKER", + "mutant": "line 22: > -> <= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "CLOUD_BASE_BELOW_LIMIT", + "CREW_ABOVE_LIMIT", + "CREW_IS_CHECKED_FIRST", + "FREE_PAYLOAD_IS_RETURNED", + "LIMITS_ARE_ACCEPTED", + "VALID_FLIGHT_IS_RELEASED", + "WEIGHT_ABOVE_LIMIT", + "WEIGHT_IS_CHECKED_SECOND", + "WIND_ABOVE_LIMIT", + "WIND_IS_CHECKED_THIRD" + ] + }, + { + "object": "{{P}}BALLOON_CHECKER", + "mutant": "line 29: > -> <= (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "CLOUD_BASE_BELOW_LIMIT", + "FREE_PAYLOAD_IS_RETURNED", + "LIMITS_ARE_ACCEPTED", + "VALID_FLIGHT_IS_RELEASED", + "WEIGHT_ABOVE_LIMIT", + "WEIGHT_IS_CHECKED_SECOND", + "WIND_ABOVE_LIMIT", + "WIND_IS_CHECKED_THIRD" + ] + }, + { + "object": "{{P}}BALLOON_CHECKER", + "mutant": "line 36: > -> <= (rel)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "CLOUD_BASE_BELOW_LIMIT", + "FREE_PAYLOAD_IS_RETURNED", + "LIMITS_ARE_ACCEPTED", + "VALID_FLIGHT_IS_RELEASED", + "WIND_ABOVE_LIMIT", + "WIND_IS_CHECKED_THIRD" + ] + }, + { + "object": "{{P}}BALLOON_CHECKER", + "mutant": "line 43: < -> >= (rel)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "CLOUD_BASE_BELOW_LIMIT", + "FREE_PAYLOAD_IS_RETURNED", + "LIMITS_ARE_ACCEPTED", + "VALID_FLIGHT_IS_RELEASED" + ] + }, + { + "object": "{{P}}BALLOON_CHECKER", + "mutant": "line 50: - -> + (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "FREE_PAYLOAD_IS_RETURNED", + "LIMITS_ARE_ACCEPTED", + "VALID_FLIGHT_IS_RELEASED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1004/reference/balloon_checker.clas.abap b/tasks_gen/train/G1004/reference/balloon_checker.clas.abap new file mode 100644 index 0000000..27e34a7 --- /dev/null +++ b/tasks_gen/train/G1004/reference/balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/reference/balloon_checker.testclasses.abap b/tasks_gen/train/G1004/reference/balloon_checker.testclasses.abap new file mode 100644 index 0000000..09ad444 --- /dev/null +++ b/tasks_gen/train/G1004/reference/balloon_checker.testclasses.abap @@ -0,0 +1,89 @@ +CLASS ltc_balloon_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}balloon_checker. + + METHODS setup. + METHODS free_payload + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i. + + METHODS released_flight FOR TESTING. + METHODS crew_limit FOR TESTING. + METHODS weight_limit FOR TESTING. + METHODS wind_limit FOR TESTING. + METHODS cloud_limit FOR TESTING. + METHODS check_sequence FOR TESTING. +ENDCLASS. + + +CLASS ltc_balloon_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}balloon_checker( ). + ENDMETHOD. + + METHOD free_payload. + TRY. + rv_free_kg = mo_cut->check_flight( iv_passengers = iv_passengers + iv_weight_kg = iv_weight_kg + iv_wind_kmh = iv_wind_kmh + iv_cloud_base_m = iv_cloud_base_m ). + CATCH {{p}}cx_balloon_flight. + rv_free_kg = -1. + ENDTRY. + ENDMETHOD. + + METHOD released_flight. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD crew_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 9 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD weight_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD wind_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD cloud_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 200 ) ). + ENDMETHOD. + + METHOD check_sequence. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/reference/cx_balloon_cloud.clas.abap b/tasks_gen/train/G1004/reference/cx_balloon_cloud.clas.abap new file mode 100644 index 0000000..6581da7 --- /dev/null +++ b/tasks_gen/train/G1004/reference/cx_balloon_cloud.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_cloud DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_cloud_base_m TYPE i + iv_limit_m TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_cloud IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Cloud base { iv_cloud_base_m } m is below the limit { iv_limit_m } m| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/reference/cx_balloon_crew.clas.abap b/tasks_gen/train/G1004/reference/cx_balloon_crew.clas.abap new file mode 100644 index 0000000..1f2c878 --- /dev/null +++ b/tasks_gen/train/G1004/reference/cx_balloon_crew.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_crew DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_passengers TYPE i + iv_limit TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_crew IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Passengers { iv_passengers } exceeds the limit { iv_limit }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/reference/cx_balloon_weight.clas.abap b/tasks_gen/train/G1004/reference/cx_balloon_weight.clas.abap new file mode 100644 index 0000000..2bd4fae --- /dev/null +++ b/tasks_gen/train/G1004/reference/cx_balloon_weight.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_weight DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_weight_kg TYPE i + iv_limit_kg TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_weight IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Weight { iv_weight_kg } kg exceeds the limit { iv_limit_kg } kg| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/reference/cx_balloon_wind.clas.abap b/tasks_gen/train/G1004/reference/cx_balloon_wind.clas.abap new file mode 100644 index 0000000..cecf001 --- /dev/null +++ b/tasks_gen/train/G1004/reference/cx_balloon_wind.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_wind DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_wind_kmh TYPE i + iv_limit_kmh TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_wind IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Wind { iv_wind_kmh } km/h exceeds the limit { iv_limit_kmh } km/h| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/seed/cx_balloon_flight.clas.abap b/tasks_gen/train/G1004/seed/cx_balloon_flight.clas.abap new file mode 100644 index 0000000..2c8c407 --- /dev/null +++ b/tasks_gen/train/G1004/seed/cx_balloon_flight.clas.abap @@ -0,0 +1,18 @@ +CLASS {{p}}cx_balloon_flight DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + + METHODS constructor + IMPORTING iv_text TYPE string. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_flight IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1004/spec.md b/tasks_gen/train/G1004/spec.md new file mode 100644 index 0000000..632d16a --- /dev/null +++ b/tasks_gen/train/G1004/spec.md @@ -0,0 +1,76 @@ +# 1. Goal +Check a hot-air balloon flight before the launch. The flight office calls the +check for each planned flight. The check must stop a flight when a limit is +exceeded. The caller needs one exception for each reason. + +# 2. Open questions +None. + +# 3. Context +- The exception class {{P}}CX_BALLOON_FLIGHT exists in package $TMP. It is + active. It inherits from CX_STATIC_CHECK. It has the public read-only + attribute MV_TEXT (type string) and a constructor with the importing + parameter IV_TEXT (type string). The constructor writes IV_TEXT to MV_TEXT. +- The flight office knows four figures of a flight: the number of passengers, + the total take-off weight in kilograms, the wind speed in kilometres per + hour, and the height of the cloud base in metres. + +# 4. Contract +- Create the class {{P}}BALLOON_CHECKER in package $TMP. + - The class is public and final. It can be created without constructor + parameters. + - The class has the public method CHECK_FLIGHT with these parameters: + - importing IV_PASSENGERS type i + - importing IV_WEIGHT_KG type i + - importing IV_WIND_KMH type i + - importing IV_CLOUD_BASE_M type i + - returning RV_FREE_KG type i + - raising {{P}}CX_BALLOON_FLIGHT + - Do not add other public methods. +- Create these exception classes in package $TMP. Each class inherits from + {{P}}CX_BALLOON_FLIGHT. + - {{P}}CX_BALLOON_CREW with the constructor importing IV_PASSENGERS type i + and IV_LIMIT type i. + - {{P}}CX_BALLOON_WEIGHT with the constructor importing IV_WEIGHT_KG type i + and IV_LIMIT_KG type i. + - {{P}}CX_BALLOON_WIND with the constructor importing IV_WIND_KMH type i and + IV_LIMIT_KMH type i. + - {{P}}CX_BALLOON_CLOUD with the constructor importing IV_CLOUD_BASE_M type i + and IV_LIMIT_M type i. + - Each constructor writes the message text to MV_TEXT. Rule 1 to rule 4 of the + business rules give the text of each exception. + +# 5. Business rules +The method CHECK_FLIGHT applies the rules in this sequence. It raises the +exception of the first rule that fails. If no rule fails, the method returns the +free payload. + +1. Passengers. The limit is 8. If IV_PASSENGERS is greater than 8, raise + {{P}}CX_BALLOON_CREW. The message text is + "Passengers exceeds the limit ". Example: 12 passengers + give the text "Passengers 12 exceeds the limit 8". +2. Weight. The limit is 640 kilograms. If IV_WEIGHT_KG is greater than 640, + raise {{P}}CX_BALLOON_WEIGHT. The message text is + "Weight kg exceeds the limit kg". Example: 700 kilograms + give the text "Weight 700 kg exceeds the limit 640 kg". +3. Wind. The limit is 18 kilometres per hour. If IV_WIND_KMH is greater than 18, + raise {{P}}CX_BALLOON_WIND. The message text is + "Wind km/h exceeds the limit km/h". Example: 25 kilometres per + hour give the text "Wind 25 km/h exceeds the limit 18 km/h". +4. Cloud base. The limit is 300 metres. If IV_CLOUD_BASE_M is less than 300, + raise {{P}}CX_BALLOON_CLOUD. The message text is + "Cloud base m is below the limit m". Example: 200 metres + give the text "Cloud base 200 m is below the limit 300 m". +5. Free payload. If no rule fails, the free payload is 640 minus IV_WEIGHT_KG. + Return the free payload in RV_FREE_KG. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}CX_BALLOON_FLIGHT. + +# 7. Acceptance +- The classes 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/G1004/task.json b/tasks_gen/train/G1004/task.json new file mode 100644 index 0000000..3f58b85 --- /dev/null +++ b/tasks_gen/train/G1004/task.json @@ -0,0 +1,90 @@ +{ + "id": "G1004", + "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_BALLOON_FLIGHT", + "file": "seed/cx_balloon_flight.clas.abap", + "description": "Base exception class of a rejected balloon flight" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}BALLOON_CHECKER" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CREW" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WEIGHT" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WIND" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CLOUD" + } + ], + "out_of_scope": [ + "{{P}}CX_BALLOON_FLIGHT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}BALLOON_HIDDEN", + "file": "hidden/balloon_hidden.clas.abap", + "description": "Hidden tests for the balloon flight check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CREW", + "file": "reference/cx_balloon_crew.clas.abap", + "description": "Too many passengers" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WEIGHT", + "file": "reference/cx_balloon_weight.clas.abap", + "description": "Weight above the limit" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WIND", + "file": "reference/cx_balloon_wind.clas.abap", + "description": "Wind above the limit" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CLOUD", + "file": "reference/cx_balloon_cloud.clas.abap", + "description": "Cloud base below the limit" + }, + { + "type": "CLAS", + "name": "{{P}}BALLOON_CHECKER", + "file": "reference/balloon_checker.clas.abap", + "description": "Balloon flight check with class-based exceptions", + "testclasses_file": "reference/balloon_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1005/faulty/m0_credit_checker.clas.abap b/tasks_gen/train/G1005/faulty/m0_credit_checker.clas.abap new file mode 100644 index 0000000..77b90cd --- /dev/null +++ b/tasks_gen/train/G1005/faulty/m0_credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income > 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) * c_income_factor + - to_cents( c_plan_reduction ) * iv_open_plans. + IF iv_age < c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/faulty/m2_credit_checker.clas.abap b/tasks_gen/train/G1005/faulty/m2_credit_checker.clas.abap new file mode 100644 index 0000000..bca059e --- /dev/null +++ b/tasks_gen/train/G1005/faulty/m2_credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income <= 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) / c_income_factor + - to_cents( c_plan_reduction ) * iv_open_plans. + IF iv_age < c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/faulty/m3_credit_checker.clas.abap b/tasks_gen/train/G1005/faulty/m3_credit_checker.clas.abap new file mode 100644 index 0000000..a954bfa --- /dev/null +++ b/tasks_gen/train/G1005/faulty/m3_credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income <= 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) * c_income_factor + - to_cents( c_plan_reduction ) / iv_open_plans. + IF iv_age < c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/faulty/m4_credit_checker.clas.abap b/tasks_gen/train/G1005/faulty/m4_credit_checker.clas.abap new file mode 100644 index 0000000..6afce5d --- /dev/null +++ b/tasks_gen/train/G1005/faulty/m4_credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income <= 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) * c_income_factor + - to_cents( c_plan_reduction ) * iv_open_plans. + IF iv_age >= c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/generation.json b/tasks_gen/train/G1005/generation.json new file mode 100644 index 0000000..2405f77 --- /dev/null +++ b/tasks_gen/train/G1005/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1005", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 96.4, + "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/G1005/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1005/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..49291fa --- /dev/null +++ b/tasks_gen/train/G1005/hidden/t02_hidden.clas.abap @@ -0,0 +1,129 @@ +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_credit_check. + METHODS setup. + METHODS too_young FOR TESTING. + METHODS age_eighteen_is_ok FOR TESTING. + METHODS zero_income FOR TESTING. + METHODS two_open_plans FOR TESTING. + METHODS order_above_limit FOR TESTING. + METHODS order_at_limit_is_ok FOR TESTING. + METHODS young_limit_halved FOR TESTING. + METHODS adult_keeps_limit FOR TESTING. + METHODS young_limit_is_floor FOR TESTING. + METHODS plan_reduces_limit FOR TESTING. + METHODS valid_adult FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}credit_checker( ). + ENDMETHOD. + + METHOD too_young. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-age + act = mo_cut->check( iv_age = 17 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD age_eighteen_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 18 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD zero_income. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-income + act = mo_cut->check( iv_age = 30 + iv_income = '0.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD two_open_plans. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-open_plan + act = mo_cut->check( iv_age = 30 + iv_income = '3000.00' + iv_open_plans = 2 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD order_above_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.01' ) ). + ENDMETHOD. + + METHOD order_at_limit_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.00' ) ). + ENDMETHOD. + + METHOD young_limit_halved. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD adult_keeps_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 21 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD young_limit_is_floor. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.50' ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.00' ) ). + ENDMETHOD. + + METHOD plan_reduces_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 1 + iv_order_amount = '2500.01' ) ). + ENDMETHOD. + + METHOD valid_adult. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 35 + iv_income = '2000.00' + iv_open_plans = 1 + iv_order_amount = '5500.00' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/mutation.json b/tasks_gen/train/G1005/mutation.json new file mode 100644 index 0000000..dead9c4 --- /dev/null +++ b/tasks_gen/train/G1005/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1005", + "mutants": [ + { + "object": "{{P}}CREDIT_CHECKER", + "mutant": "line 30: <= -> > (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "ADULT_KEEPS_LIMIT", + "AGE_EIGHTEEN_IS_OK", + "ORDER_ABOVE_LIMIT", + "ORDER_AT_LIMIT_IS_OK", + "PLAN_REDUCES_LIMIT", + "TWO_OPEN_PLANS", + "VALID_ADULT", + "YOUNG_LIMIT_HALVED", + "YOUNG_LIMIT_IS_FLOOR", + "ZERO_INCOME" + ] + }, + { + "object": "{{P}}CREDIT_CHECKER", + "mutant": "line 38: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}CREDIT_CHECKER", + "mutant": "line 48: * -> / (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "ADULT_KEEPS_LIMIT", + "ORDER_AT_LIMIT_IS_OK", + "VALID_ADULT", + "YOUNG_LIMIT_IS_FLOOR" + ] + }, + { + "object": "{{P}}CREDIT_CHECKER", + "mutant": "line 49: * -> / (arith)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "ADULT_KEEPS_LIMIT", + "AGE_EIGHTEEN_IS_OK", + "ORDER_ABOVE_LIMIT", + "ORDER_AT_LIMIT_IS_OK", + "YOUNG_LIMIT_HALVED", + "YOUNG_LIMIT_IS_FLOOR" + ] + }, + { + "object": "{{P}}CREDIT_CHECKER", + "mutant": "line 50: < -> >= (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "ADULT_KEEPS_LIMIT", + "ORDER_AT_LIMIT_IS_OK", + "VALID_ADULT", + "YOUNG_LIMIT_HALVED", + "YOUNG_LIMIT_IS_FLOOR" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1005/reference/credit_checker.clas.abap b/tasks_gen/train/G1005/reference/credit_checker.clas.abap new file mode 100644 index 0000000..49d5113 --- /dev/null +++ b/tasks_gen/train/G1005/reference/credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income <= 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) * c_income_factor + - to_cents( c_plan_reduction ) * iv_open_plans. + IF iv_age < c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/reference/credit_checker.testclasses.abap b/tasks_gen/train/G1005/reference/credit_checker.testclasses.abap new file mode 100644 index 0000000..e1b8a57 --- /dev/null +++ b/tasks_gen/train/G1005/reference/credit_checker.testclasses.abap @@ -0,0 +1,98 @@ +CLASS ltc_credit_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_credit_check. + METHODS setup. + METHODS too_young FOR TESTING. + METHODS zero_income FOR TESTING. + METHODS two_open_plans FOR TESTING. + METHODS order_above_limit FOR TESTING. + METHODS order_at_limit FOR TESTING. + METHODS young_limit_halved FOR TESTING. + METHODS young_limit_is_floor FOR TESTING. + METHODS valid_adult FOR TESTING. +ENDCLASS. + + +CLASS ltc_credit_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}credit_checker( ). + ENDMETHOD. + + METHOD too_young. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-age + act = mo_cut->check( iv_age = 17 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD zero_income. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-income + act = mo_cut->check( iv_age = 30 + iv_income = '0.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD two_open_plans. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-open_plan + act = mo_cut->check( iv_age = 30 + iv_income = '3000.00' + iv_open_plans = 2 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD order_above_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.01' ) ). + ENDMETHOD. + + METHOD order_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.00' ) ). + ENDMETHOD. + + METHOD young_limit_halved. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD young_limit_is_floor. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.50' ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.00' ) ). + ENDMETHOD. + + METHOD valid_adult. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 35 + iv_income = '2000.00' + iv_open_plans = 1 + iv_order_amount = '5500.00' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1005/seed/if_credit_check.intf.abap b/tasks_gen/train/G1005/seed/if_credit_check.intf.abap new file mode 100644 index 0000000..2175a55 --- /dev/null +++ b/tasks_gen/train/G1005/seed/if_credit_check.intf.abap @@ -0,0 +1,18 @@ +INTERFACE {{p}}if_credit_check PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 11 DECIMALS 2. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + age TYPE ty_reason VALUE 'AGE', + income TYPE ty_reason VALUE 'INCOME', + open_plan TYPE ty_reason VALUE 'OPEN_PLAN', + limit TYPE ty_reason VALUE 'LIMIT', + END OF c_reason. + METHODS check + IMPORTING iv_age TYPE i + iv_income TYPE ty_amount + iv_open_plans TYPE i + iv_order_amount TYPE ty_amount + RETURNING VALUE(rv_reason) TYPE ty_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1005/spec.md b/tasks_gen/train/G1005/spec.md new file mode 100644 index 0000000..786330f --- /dev/null +++ b/tasks_gen/train/G1005/spec.md @@ -0,0 +1,56 @@ +# 1. Goal + +A furniture shop sells furniture on instalment plans. A checkout program calls one +check before the shop grants an instalment plan to a customer. The program needs +one reason code for each customer. + +# 2. Open questions + +None. + +# 3. Context + +- The interface {{P}}IF_CREDIT_CHECK exists in package $TMP. It is active. +- The interface declares the type of the reason, the type of a money amount, the + constants of the reason codes, and one method. +- The method name, the parameter names and the parameter types are not in this + document. Read the interface in the system. +- The reason constants have the names OK, AGE, INCOME, OPEN_PLAN and LIMIT. + +# 4. Contract + +- Create the class {{P}}CREDIT_CHECKER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class implements the interface {{P}}IF_CREDIT_CHECK. +- Do not add other public methods. + +# 5. Business rules + +The check receives the age of the customer in years, the net monthly income of the +customer, the number of open instalment plans of the customer, and the amount of +the new order. Apply the rules in this sequence. Return the reason of the first +rule that fails. + +1. If the age is less than 18, return AGE. +2. If the net monthly income is zero or less, return INCOME. +3. If the number of open instalment plans is 2 or more, return OPEN_PLAN. +4. Calculate the credit limit: + - Start with three times the net monthly income. + - Subtract 500 for each open instalment plan. + - If the age is less than 21, halve the limit and round the result down to + whole euros. Example: 3750.75 becomes 3750.00. +5. If the amount of the new order is more than the credit limit, return LIMIT. +6. Else return OK. + +# 6. Constraints + +- Release target: SAP_BASIS 8.16 (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 the interface {{P}}IF_CREDIT_CHECK. + +# 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/G1005/task.json b/tasks_gen/train/G1005/task.json new file mode 100644 index 0000000..78d6ad4 --- /dev/null +++ b/tasks_gen/train/G1005/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1005", + "category": "F", + "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_CREDIT_CHECK", + "file": "seed/if_credit_check.intf.abap", + "description": "Instalment plan check contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CREDIT_CHECKER", + "implements": "{{P}}IF_CREDIT_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_CREDIT_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CREDIT_CHECKER", + "file": "reference/credit_checker.clas.abap", + "description": "Instalment plan credit checker", + "testclasses_file": "reference/credit_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1006/faulty/m0_auc_premium.func.abap b/tasks_gen/train/G1006/faulty/m0_auc_premium.func.abap new file mode 100644 index 0000000..1514b2c --- /dev/null +++ b/tasks_gen/train/G1006/faulty/m0_auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_true. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/faulty/m1_auc_premium.func.abap b/tasks_gen/train/G1006/faulty/m1_auc_premium.func.abap new file mode 100644 index 0000000..f479544 --- /dev/null +++ b/tasks_gen/train/G1006/faulty/m1_auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer > 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/faulty/m2_auc_premium.func.abap b/tasks_gen/train/G1006/faulty/m2_auc_premium.func.abap new file mode 100644 index 0000000..8bed961 --- /dev/null +++ b/tasks_gen/train/G1006/faulty/m2_auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 101 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/faulty/m3_auc_premium.func.abap b/tasks_gen/train/G1006/faulty/m3_auc_premium.func.abap new file mode 100644 index 0000000..2ad915e --- /dev/null +++ b/tasks_gen/train/G1006/faulty/m3_auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel <> 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/faulty/m4_auc_premium.func.abap b/tasks_gen/train/G1006/faulty/m4_auc_premium.func.abap new file mode 100644 index 0000000..b10fa2c --- /dev/null +++ b/tasks_gen/train/G1006/faulty/m4_auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium / 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/generation.json b/tasks_gen/train/G1006/generation.json new file mode 100644 index 0000000..782bc6d --- /dev/null +++ b/tasks_gen/train/G1006/generation.json @@ -0,0 +1,60 @@ +{ + "id": "G1006", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 47: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 53: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 59: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 65: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 71: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 77: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 89: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 96: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "Too close to task G0150 (similarity spec 0.60, rules 0.63, names 0.25). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.70, rules 0.63, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0173 (similarity spec 0.65, rules 0.62, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 9, + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Invalid \\escape: line 1 column 1374 (char 1373)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 82: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"15\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"56\"" + ], + "by_check": { + "abaplint": 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/G1006/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1006/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..b6c716b --- /dev/null +++ b/tasks_gen/train/G1006/hidden/t14_hidden.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS band_first FOR TESTING. + METHODS band_second FOR TESTING. + METHODS band_third FOR TESTING. + METHODS online_reduction FOR TESTING. + METHODS premium_rounding FOR TESTING. + METHODS minimum_premium FOR TESTING. + METHODS maximum_premium FOR TESTING. + METHODS charity_skips FOR TESTING. + METHODS negative_hammer FOR TESTING. + METHODS skip_false_for_sale FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = iv_lot_type + iv_hammer = iv_hammer + iv_channel = iv_channel + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD band_first. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 100. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD band_second. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 320. + call( EXPORTING iv_lot_type = `S` iv_hammer = 2000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD band_third. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 1880. + call( EXPORTING iv_lot_type = `S` iv_hammer = 20000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD online_reduction. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 90. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `O` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD premium_rounding. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + DATA lv_hammer TYPE decfloat34. + lv_exp = '228.15'. + lv_hammer = '1234.56'. + call( EXPORTING iv_lot_type = `S` iv_hammer = lv_hammer iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD minimum_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5. + call( EXPORTING iv_lot_type = `S` iv_hammer = 10 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD maximum_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5000. + call( EXPORTING iv_lot_type = `S` iv_hammer = 100000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD charity_skips. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_fee = 7. + lv_exp = 7. + call( EXPORTING iv_lot_type = `C` iv_hammer = 5000 iv_channel = `B` 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 negative_hammer. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lot_type = `S` iv_hammer = -1 iv_channel = `B` + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD skip_false_for_sale. + DATA lv_skip TYPE abap_bool. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `B` + IMPORTING ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1006/mutation.json b/tasks_gen/train/G1006/mutation.json new file mode 100644 index 0000000..85892ae --- /dev/null +++ b/tasks_gen/train/G1006/mutation.json @@ -0,0 +1,60 @@ +{ + "task": "G1006", + "mutants": [ + { + "object": "{{P}}AUC_PREMIUM", + "mutant": "line 15: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "SKIP_FALSE_FOR_SALE" + ] + }, + { + "object": "{{P}}AUC_PREMIUM", + "mutant": "line 25: <= -> > (rel)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "BAND_SECOND", + "BAND_THIRD", + "PREMIUM_ROUNDING" + ] + }, + { + "object": "{{P}}AUC_PREMIUM", + "mutant": "line 28: 100 -> 101 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "BAND_THIRD" + ] + }, + { + "object": "{{P}}AUC_PREMIUM", + "mutant": "line 30: = -> <> (eq)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "BAND_FIRST", + "BAND_SECOND", + "BAND_THIRD", + "ONLINE_REDUCTION", + "PREMIUM_ROUNDING" + ] + }, + { + "object": "{{P}}AUC_PREMIUM", + "mutant": "line 31: * -> / (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "ONLINE_REDUCTION" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1006/reference/auc_premium.func.abap b/tasks_gen/train/G1006/reference/auc_premium.func.abap new file mode 100644 index 0000000..d603665 --- /dev/null +++ b/tasks_gen/train/G1006/reference/auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/reference/t14_test.clas.abap b/tasks_gen/train/G1006/reference/t14_test.clas.abap new file mode 100644 index 0000000..7883737 --- /dev/null +++ b/tasks_gen/train/G1006/reference/t14_test.clas.abap @@ -0,0 +1,99 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS premium_for + IMPORTING iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS band_premium FOR TESTING. + METHODS online_reduction FOR TESTING. + METHODS premium_limits FOR TESTING. + METHODS charity_lot FOR TESTING. + METHODS negative_hammer FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD premium_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = iv_lot_type + iv_hammer = iv_hammer + iv_channel = iv_channel + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD band_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 320. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 2000 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD online_reduction. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 90. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 500 iv_channel = `O` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD premium_limits. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 10 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + lv_exp = 5000. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 100000 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD charity_lot. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_fee = 7. + lv_exp = 7. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = `C` + iv_hammer = 5000 + iv_channel = `B` + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD negative_hammer. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = `S` + iv_hammer = -5 + iv_channel = `B` + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1006/seed/auc_sample.func.abap b/tasks_gen/train/G1006/seed/auc_sample.func.abap new file mode 100644 index 0000000..73c393f --- /dev/null +++ b/tasks_gen/train/G1006/seed/auc_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}auc_sample + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1006/spec.md b/tasks_gen/train/G1006/spec.md new file mode 100644 index 0000000..3f26ff4 --- /dev/null +++ b/tasks_gen/train/G1006/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +An auction house charges a buyer's premium on each lot that it sells. The +auction program calls one premium module for each sold lot. The premium module +must use the same interface as the sample module {{P}}AUC_SAMPLE, because the +auction program calls all premium modules in the same way. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_AUCT exists in package $TMP. +- Function module {{P}}AUC_SAMPLE exists in this group. It is the sample + premium module. Its interface is the interface that all premium modules must + use. + +# 4. Contract +- Create the function module {{P}}AUC_PREMIUM in function group {{P}}FG_AUCT. +- Use exactly the interface of {{P}}AUC_SAMPLE: the same parameter names, kinds, + types, and exceptions. +- The exception is INVALID_HAMMER. + +# 5. Business rules +1. If the hammer price is less than 0, raise the exception INVALID_HAMMER and + stop. +2. Set the skip flag to false, except in rule 3. +3. Lot type C (charity): set the skip flag to true. Leave the fee unchanged and + stop. +4. For every other lot type, calculate the band premium from the hammer price: + - the part of the hammer price up to 1,000.00: 20 % + - the part of the hammer price above 1,000.00 up to 10,000.00: 12 % + - the part of the hammer price above 10,000.00: 6 % +5. Channel O (online): the premium is 90 % of the band premium. For every other + channel the premium is the band premium. +6. Round the premium of rule 5 half up to 2 decimals. +7. The minimum premium is 5.00. The maximum premium is 5,000.00. +8. The fee is the premium of rules 4 to 7. + +# 6. Constraints +- Release target: 7.02. The code must run on a 7.02 system. Do not use syntax + that was introduced after release 7.02. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}AUC_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/G1006/task.json b/tasks_gen/train/G1006/task.json new file mode 100644 index 0000000..3811a0e --- /dev/null +++ b/tasks_gen/train/G1006/task.json @@ -0,0 +1,84 @@ +{ + "id": "G1006", + "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_AUCT", + "description": "Buyer's premium modules" + }, + { + "type": "FUNC", + "name": "{{P}}AUC_SAMPLE", + "functionGroup": "{{P}}FG_AUCT", + "file": "seed/auc_sample.func.abap", + "description": "Sample premium module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}AUC_PREMIUM", + "functionGroup": "{{P}}FG_AUCT", + "params": [ + { + "name": "iv_lot_type", + "type": "string" + }, + { + "name": "iv_hammer", + "type": "decfloat34" + }, + { + "name": "iv_channel", + "type": "string" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}AUC_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}AUC_PREMIUM", + "functionGroup": "{{P}}FG_AUCT", + "file": "reference/auc_premium.func.abap", + "description": "Buyer's premium module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "no_new_syntax" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1007/generation.json b/tasks_gen/train/G1007/generation.json new file mode 100644 index 0000000..15ea044 --- /dev/null +++ b/tasks_gen/train/G1007/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1007", + "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/G1007/reference/jump_pricer.clas.abap b/tasks_gen/train/G1007/reference/jump_pricer.clas.abap new file mode 100644 index 0000000..e6a4b78 --- /dev/null +++ b/tasks_gen/train/G1007/reference/jump_pricer.clas.abap @@ -0,0 +1,56 @@ +CLASS {{p}}jump_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate + IMPORTING iv_persons TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_short_price TYPE ty_amount VALUE '8.00', + c_long_price TYPE ty_amount VALUE '12.00', + c_member_factor TYPE ty_amount VALUE '0.80', + c_group_factor TYPE ty_amount VALUE '0.90', + c_group_size TYPE i VALUE 10, + c_long_minutes TYPE i VALUE 60. + + METHODS price_per_person + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}jump_pricer IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate. + IF iv_persons < 1 OR iv_minutes < 1. + RETURN. + ENDIF. + + DATA(lv_price) = CONV ty_amount( price_per_person( iv_minutes ) * iv_persons ). + + IF iv_member = abap_true. + lv_price = lv_price * c_member_factor. + ENDIF. + + IF iv_persons >= c_group_size. + lv_price = lv_price * c_group_factor. + ENDIF. + + rv_price = round( val = lv_price dec = 2 ). + ENDMETHOD. + + METHOD price_per_person. + IF iv_minutes < c_long_minutes. + rv_price = c_short_price. + ELSE. + rv_price = c_long_price. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1007/reference/jump_pricer.testclasses.abap b/tasks_gen/train/G1007/reference/jump_pricer.testclasses.abap new file mode 100644 index 0000000..4e48712 --- /dev/null +++ b/tasks_gen/train/G1007/reference/jump_pricer.testclasses.abap @@ -0,0 +1,61 @@ +CLASS ltc_jump_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}jump_pricer. + METHODS setup. + METHODS zero_persons FOR TESTING. + METHODS zero_minutes FOR TESTING. + METHODS short_session FOR TESTING. + METHODS long_session FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS member_and_group FOR TESTING. +ENDCLASS. + + +CLASS ltc_jump_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}jump_pricer( ). + ENDMETHOD. + + METHOD zero_persons. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '0.00' ) + act = mo_cut->calculate( iv_persons = 0 iv_minutes = 30 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD zero_minutes. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '0.00' ) + act = mo_cut->calculate( iv_persons = 4 iv_minutes = 0 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD short_session. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '16.00' ) + act = mo_cut->calculate( iv_persons = 2 iv_minutes = 30 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD long_session. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '36.00' ) + act = mo_cut->calculate( iv_persons = 3 iv_minutes = 90 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD member_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '12.80' ) + act = mo_cut->calculate( iv_persons = 2 iv_minutes = 30 iv_member = abap_true ) ). + ENDMETHOD. + + METHOD group_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '72.00' ) + act = mo_cut->calculate( iv_persons = 10 iv_minutes = 30 iv_member = abap_false ) ). + ENDMETHOD. + + METHOD member_and_group. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}jump_pricer=>ty_amount( '86.40' ) + act = mo_cut->calculate( iv_persons = 10 iv_minutes = 90 iv_member = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1007/spec.md b/tasks_gen/train/G1007/spec.md new file mode 100644 index 0000000..9e30ce6 --- /dev/null +++ b/tasks_gen/train/G1007/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +Calculate the price of a jump session at a trampoline park. A booking program +calls the calculation before it confirms a booking. The booking program needs +one price in euro for each booking. + +# 2. Open questions +None. + +# 3. Context +- The trampoline park sells jump sessions of 30, 45, 60, 75 or 90 minutes. +- The booking program knows the number of persons in the booking, the length of + the session in minutes, and if the customer has a member card. +- All prices are in euro and have 2 decimals. + +# 4. Contract +- Create the class {{P}}JUMP_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The public section has the type TY_AMOUNT (packed number, length 8, 2 decimals) + and one method: + CALCULATE + IMPORTING iv_persons TYPE i + iv_minutes TYPE i + iv_member TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_amount. +- iv_persons is the number of persons. iv_minutes is the session length in + minutes. iv_member is abap_true if the customer has a member card. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. If iv_persons is less than 1, or iv_minutes is less than 1, the price is 0.00. +2. The price per person depends on the session length: + - If the session is shorter than 60 minutes, the price per person is 8.00. + - If the session is longer than 60 minutes, the price per person is 12.00. +3. Multiply the price per person by the number of persons. +4. If iv_member is abap_true, subtract 20 percent from the result of rule 3. +5. If the number of persons is 10 or more, subtract 10 percent from the result + of rule 4. +6. Round the result to 2 decimals. Round half up. + +# 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 other objects in package $TMP. + +# 7. Acceptance +- The class is active and has no syntax error. +- Every business rule is checked by a test. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1007/task.json b/tasks_gen/train/G1007/task.json new file mode 100644 index 0000000..ebfd27f --- /dev/null +++ b/tasks_gen/train/G1007/task.json @@ -0,0 +1,54 @@ +{ + "id": "G1007", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The business rules do not define the price per person for a jump session of exactly 60 minutes.", + "keywords": [ + [ + "exactly", + "60" + ], + [ + "60", + "minutes" + ], + [ + "60", + "minute" + ], + [ + "boundary", + "60" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}JUMP_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}JUMP_PRICER", + "file": "reference/jump_pricer.clas.abap", + "description": "Jump session pricer", + "testclasses_file": "reference/jump_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1008/generation.json b/tasks_gen/train/G1008/generation.json new file mode 100644 index 0000000..5518689 --- /dev/null +++ b/tasks_gen/train/G1008/generation.json @@ -0,0 +1,33 @@ +{ + "id": "G1008", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/turb_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 73.0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1008/hidden/turb_hidden.clas.abap b/tasks_gen/train/G1008/hidden/turb_hidden.clas.abap new file mode 100644 index 0000000..7a0897d --- /dev/null +++ b/tasks_gen/train/G1008/hidden/turb_hidden.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}turb_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 revenue_of_turbine FOR TESTING. + METHODS revenue_other_park FOR TESTING. + METHODS revenue_rounded FOR TESTING. + METHODS park_name_via_path FOR TESTING. + METHODS country_via_path FOR TESTING. + METHODS filter_by_country FOR TESTING. + METHODS row_per_turbine FOR TESTING. + METHODS nominal_kw_value FOR TESTING. + METHODS yield_value FOR TESTING. + METHODS park_id_value FOR TESTING. +ENDCLASS. + + +CLASS {{p}}turb_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TURBINE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_park TYPE STANDARD TABLE OF {{p}}wpark WITH EMPTY KEY. + DATA lt_turb TYPE STANDARD TABLE OF {{p}}turbine WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_park = VALUE #( ( park_id = 'P1' park_name = 'North Ridge' country = 'DE' tariff = '82.30' ) + ( park_id = 'P2' park_name = 'Sunny Hills' country = 'AT' tariff = '50.00' ) + ( park_id = 'P3' park_name = 'Coastal Wind' country = 'NL' tariff = '12.34' ) ). + lt_turb = VALUE #( ( turbine_id = 'T1' park_id = 'P1' nominal_kw = 3000 yield_mwh = '1234.50' ) + ( turbine_id = 'T2' park_id = 'P1' nominal_kw = 2000 yield_mwh = '100.00' ) + ( turbine_id = 'T3' park_id = 'P3' nominal_kw = 1500 yield_mwh = '0.50' ) + ( turbine_id = 'T4' park_id = 'P2' nominal_kw = 2500 yield_mwh = '10.00' ) + ( turbine_id = 'T5' park_id = 'P3' nominal_kw = 1000 yield_mwh = '0.33' ) ). + go_env->insert_test_data( lt_park ). + go_env->insert_test_data( lt_turb ). + ENDMETHOD. + + METHOD revenue_of_turbine. + SELECT SINGLE revenue FROM {{p}}i_turbine WHERE turbine_id = 'T1' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '101599.35' ) + act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. + + METHOD revenue_other_park. + SELECT SINGLE revenue FROM {{p}}i_turbine WHERE turbine_id = 'T4' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '500.00' ) + act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. + + METHOD revenue_rounded. + SELECT SINGLE revenue FROM {{p}}i_turbine WHERE turbine_id = 'T5' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '4.07' ) + act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. + + METHOD park_name_via_path. + SELECT SINGLE \_PARK-park_name FROM {{p}}i_turbine + WHERE turbine_id = 'T1' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'North Ridge' act = lv_name ). + ENDMETHOD. + + METHOD country_via_path. + SELECT SINGLE \_PARK-country FROM {{p}}i_turbine + WHERE turbine_id = 'T3' INTO @DATA(lv_country). + cl_abap_unit_assert=>assert_equals( exp = 'NL' act = lv_country ). + ENDMETHOD. + + METHOD filter_by_country. + SELECT turbine_id, \_PARK-country AS country + FROM {{p}}i_turbine INTO TABLE @DATA(lt_rows). + DELETE lt_rows WHERE country <> 'DE'. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_rows ) ). + ENDMETHOD. + + METHOD row_per_turbine. + SELECT turbine_id FROM {{p}}i_turbine INTO TABLE @DATA(lt_all). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_all ) ). + ENDMETHOD. + + METHOD nominal_kw_value. + SELECT SINGLE nominal_kw FROM {{p}}i_turbine WHERE turbine_id = 'T2' INTO @DATA(lv_kw). + cl_abap_unit_assert=>assert_equals( exp = 2000 act = lv_kw ). + ENDMETHOD. + + METHOD yield_value. + SELECT SINGLE yield_mwh FROM {{p}}i_turbine WHERE turbine_id = 'T3' INTO @DATA(lv_yield). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.50' ) + act = CONV decfloat34( lv_yield ) ). + ENDMETHOD. + + METHOD park_id_value. + SELECT SINGLE park_id FROM {{p}}i_turbine WHERE turbine_id = 'T4' INTO @DATA(lv_park). + cl_abap_unit_assert=>assert_equals( exp = 'P2' act = lv_park ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1008/mutation.json b/tasks_gen/train/G1008/mutation.json new file mode 100644 index 0000000..48a8b3c --- /dev/null +++ b/tasks_gen/train/G1008/mutation.json @@ -0,0 +1,42 @@ +{ + "task": "T16", + "mutants": [ + { + "object": "{{P}}I_TURBINE", + "mutant": "revenue: * -> + (arithmetic)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "REVENUE_OF_TURBINE", + "REVENUE_OTHER_PARK", + "REVENUE_ROUNDED" + ] + }, + { + "object": "{{P}}I_TURBINE", + "mutant": "association ON condition: = -> <> (eq)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "PARK_NAME_VIA_PATH", + "COUNTRY_VIA_PATH", + "FILTER_BY_COUNTRY" + ] + }, + { + "object": "{{P}}I_TURBINE", + "mutant": "select list: _PARK removed (association not exposed)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "PARK_NAME_VIA_PATH", + "COUNTRY_VIA_PATH", + "FILTER_BY_COUNTRY" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1008/reference/i_turbine.ddls.asddls b/tasks_gen/train/G1008/reference/i_turbine.ddls.asddls new file mode 100644 index 0000000..ba60875 --- /dev/null +++ b/tasks_gen/train/G1008/reference/i_turbine.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Wind turbine revenue' +define view entity {{P}}I_TURBINE + as select from {{p}}turbine as turbine + left outer join {{p}}wpark as park on park.park_id = turbine.park_id + association [0..1] to {{p}}wpark as _PARK on $projection.park_id = _PARK.park_id +{ + key turbine.turbine_id as turbine_id, + turbine.park_id as park_id, + turbine.nominal_kw as nominal_kw, + turbine.yield_mwh as yield_mwh, + cast( turbine.yield_mwh * park.tariff as abap.dec(15,2) ) as revenue, + _PARK +} diff --git a/tasks_gen/train/G1008/reference/turb_test.clas.abap b/tasks_gen/train/G1008/reference/turb_test.clas.abap new file mode 100644 index 0000000..086df6b --- /dev/null +++ b/tasks_gen/train/G1008/reference/turb_test.clas.abap @@ -0,0 +1,41 @@ +CLASS {{p}}turb_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 revenue_and_park FOR TESTING. +ENDCLASS. + + +CLASS {{p}}turb_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TURBINE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD revenue_and_park. + DATA lt_park TYPE STANDARD TABLE OF {{p}}wpark WITH EMPTY KEY. + DATA lt_turb TYPE STANDARD TABLE OF {{p}}turbine WITH EMPTY KEY. + lt_park = VALUE #( ( park_id = 'A' park_name = 'Alpha Park' country = 'DE' tariff = '10.00' ) ). + lt_turb = VALUE #( ( turbine_id = 'X' park_id = 'A' nominal_kw = 100 yield_mwh = '5.00' ) ). + go_env->insert_test_data( lt_park ). + go_env->insert_test_data( lt_turb ). + + SELECT SINGLE revenue FROM {{p}}i_turbine WHERE turbine_id = 'X' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '50.00' ) + act = CONV decfloat34( lv_revenue ) ). + + SELECT SINGLE \_PARK-park_name FROM {{p}}i_turbine + WHERE turbine_id = 'X' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'Alpha Park' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1008/seed/turbine.tabl.asabap b/tasks_gen/train/G1008/seed/turbine.tabl.asabap new file mode 100644 index 0000000..dec7efa --- /dev/null +++ b/tasks_gen/train/G1008/seed/turbine.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Wind turbine' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}turbine { + key client : abap.clnt not null; + key turbine_id : abap.char(10) not null; + park_id : abap.char(10); + nominal_kw : abap.int4; + yield_mwh : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1008/seed/wpark.tabl.asabap b/tasks_gen/train/G1008/seed/wpark.tabl.asabap new file mode 100644 index 0000000..12f3051 --- /dev/null +++ b/tasks_gen/train/G1008/seed/wpark.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Wind park' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}wpark { + key client : abap.clnt not null; + key park_id : abap.char(10) not null; + park_name : abap.char(40); + country : abap.char(3); + tariff : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1008/spec.md b/tasks_gen/train/G1008/spec.md new file mode 100644 index 0000000..1b9adf9 --- /dev/null +++ b/tasks_gen/train/G1008/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +A wind farm operator needs the revenue of each wind turbine. The revenue is the +yield of the turbine multiplied by the feed-in tariff of the wind park of the +turbine. A report and an OData service read the revenue, so the calculation must +be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}WPARK (wind park), package $TMP. Key: PARK_ID (CHAR 10). + Fields: PARK_NAME (CHAR 40), COUNTRY (CHAR 3), TARIFF (DEC 15,2). TARIFF is + the feed-in tariff in euro for one MWh. +- Table {{P}}TURBINE (wind turbine), package $TMP. Key: TURBINE_ID (CHAR 10). + Fields: PARK_ID (CHAR 10), NOMINAL_KW (INT4), YIELD_MWH (DEC 15,2). YIELD_MWH + is the yield of the turbine in MWh. + +# 4. Contract +- Create the CDS view entity {{P}}I_TURBINE in package $TMP. +- Elements, with these names: TURBINE_ID (key), PARK_ID, NOMINAL_KW, YIELD_MWH, + REVENUE. +- The view exposes an association with the name _PARK. The target of _PARK is + the wind park of the turbine. +- A caller reads the data of the wind park from the view with a path expression, + for example \_PARK-PARK_NAME or \_PARK-COUNTRY. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each turbine in {{P}}TURBINE. +2. PARK_ID is the park of the turbine. +3. NOMINAL_KW is the nominal power of the turbine. +4. YIELD_MWH is the yield of the turbine. +5. REVENUE is YIELD_MWH multiplied by the TARIFF of the wind park of the + turbine. REVENUE has 2 decimals. +6. _PARK points to the row of {{P}}WPARK that has the same PARK_ID as the + turbine. +7. PARK_NAME, COUNTRY and TARIFF of the wind park are available through _PARK. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}WPARK and {{P}}TURBINE. + +# 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/G1008/task.json b/tasks_gen/train/G1008/task.json new file mode 100644 index 0000000..f4a8d89 --- /dev/null +++ b/tasks_gen/train/G1008/task.json @@ -0,0 +1,66 @@ +{ + "id": "G1008", + "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}}WPARK", + "file": "seed/wpark.tabl.asabap", + "description": "Wind park" + }, + { + "type": "TABL", + "name": "{{P}}TURBINE", + "file": "seed/turbine.tabl.asabap", + "description": "Wind turbine" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_TURBINE", + "fields": [ + "TURBINE_ID", + "PARK_ID", + "NOMINAL_KW", + "YIELD_MWH", + "REVENUE" + ] + } + ], + "out_of_scope": [ + "{{P}}WPARK", + "{{P}}TURBINE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TURB_HIDDEN", + "file": "hidden/turb_hidden.clas.abap", + "description": "Hidden tests for {{P}}I_TURBINE" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_TURBINE", + "file": "reference/i_turbine.ddls.asddls", + "description": "Wind turbine revenue" + }, + { + "type": "CLAS", + "name": "{{P}}TURB_TEST", + "file": "reference/turb_test.clas.abap", + "description": "Own tests for {{P}}I_TURBINE" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1009/faulty/m0_fish_auction.clas.abap b/tasks_gen/train/G1009/faulty/m0_fish_auction.clas.abap new file mode 100644 index 0000000..62fefcc --- /dev/null +++ b/tasks_gen/train/G1009/faulty/m0_fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL AND ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/faulty/m1_fish_auction.clas.abap b/tasks_gen/train/G1009/faulty/m1_fish_auction.clas.abap new file mode 100644 index 0000000..bdc9fe2 --- /dev/null +++ b/tasks_gen/train/G1009/faulty/m1_fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg > 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/faulty/m2_fish_auction.clas.abap b/tasks_gen/train/G1009/faulty/m2_fish_auction.clas.abap new file mode 100644 index 0000000..f105ae8 --- /dev/null +++ b/tasks_gen/train/G1009/faulty/m2_fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight - ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/faulty/m3_fish_auction.clas.abap b/tasks_gen/train/G1009/faulty/m3_fish_auction.clas.abap new file mode 100644 index 0000000..ca31ba6 --- /dev/null +++ b/tasks_gen/train/G1009/faulty/m3_fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count - 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/faulty/m4_fish_auction.clas.abap b/tasks_gen/train/G1009/faulty/m4_fish_auction.clas.abap new file mode 100644 index 0000000..95e0b0e --- /dev/null +++ b/tasks_gen/train/G1009/faulty/m4_fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight <= lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/generation.json b/tasks_gen/train/G1009/generation.json new file mode 100644 index 0000000..ecf6f3e --- /dev/null +++ b/tasks_gen/train/G1009/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1009", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 63.4, + "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/G1009/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1009/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..b6b8796 --- /dev/null +++ b/tasks_gen/train/G1009/hidden/t31_hidden.clas.abap @@ -0,0 +1,140 @@ +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}}fish_auction. + METHODS setup. + METHODS sums_two_species FOR TESTING. + METHODS counts_distinct_boats FOR TESTING. + METHODS ignores_zero_weight FOR TESTING. + METHODS ignores_blank_species FOR TESTING. + METHODS trims_and_uppercases FOR TESTING. + METHODS sorts_by_species FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS all_entries_invalid FOR TESTING. + METHODS best_species_highest FOR TESTING. + METHODS best_species_tie FOR TESTING. + METHODS best_species_none FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}fish_auction( ). + ENDMETHOD. + + METHOD sums_two_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 100 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 50 ) + ( boat_id = 'B1' species = 'HERRING' weight_kg = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 150 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + READ TABLE lt_result INTO ls_row WITH KEY species = 'HERRING'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 1 ). + ENDMETHOD. + + METHOD counts_distinct_boats. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B1' species = 'COD' weight_kg = 20 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 5 ) ) ). + + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 35 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD ignores_zero_weight. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 0 ) + ( boat_id = 'B2' species = 'COD' weight_kg = -5 ) + ( boat_id = 'B3' species = 'COD' weight_kg = 7 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 7 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 1 ). + ENDMETHOD. + + METHOD ignores_blank_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = '' weight_kg = 10 ) + ( boat_id = 'B2' species = ' ' weight_kg = 20 ) + ( boat_id = 'B3' species = 'COD' weight_kg = 5 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 5 ). + ENDMETHOD. + + METHOD trims_and_uppercases. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' cod ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD sorts_by_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 20 ) + ( boat_id = 'B3' species = 'MACKEREL' weight_kg = 5 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 3 ). + READ TABLE lt_result INTO DATA(ls_row) INDEX 1. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'COD' ). + READ TABLE lt_result INTO ls_row INDEX 3. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'TUNA' ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD all_entries_invalid. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD best_species_highest. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA' weight_kg = 40 ) + ( boat_id = 'B3' species = 'HERRING' weight_kg = 25 ) ) ) + exp = 'TUNA' ). + ENDMETHOD. + + METHOD best_species_tie. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 30 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 30 ) ) ) + exp = 'COD' ). + ENDMETHOD. + + METHOD best_species_none. + cl_abap_unit_assert=>assert_initial( mo_cut->best_species( VALUE #( ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/mutation.json b/tasks_gen/train/G1009/mutation.json new file mode 100644 index 0000000..ae0d63c --- /dev/null +++ b/tasks_gen/train/G1009/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G1009", + "mutants": [ + { + "object": "{{P}}FISH_AUCTION", + "mutant": "line 55: OR -> AND (logic)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "ALL_ENTRIES_INVALID", + "IGNORES_BLANK_SPECIES", + "IGNORES_ZERO_WEIGHT" + ] + }, + { + "object": "{{P}}FISH_AUCTION", + "mutant": "line 55: <= -> > (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "ALL_ENTRIES_INVALID", + "BEST_SPECIES_HIGHEST", + "BEST_SPECIES_TIE", + "COUNTS_DISTINCT_BOATS", + "IGNORES_BLANK_SPECIES", + "IGNORES_ZERO_WEIGHT", + "SORTS_BY_SPECIES", + "SUMS_TWO_SPECIES", + "TRIMS_AND_UPPERCASES" + ] + }, + { + "object": "{{P}}FISH_AUCTION", + "mutant": "line 66: + -> - (arith)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "BEST_SPECIES_HIGHEST", + "BEST_SPECIES_TIE", + "COUNTS_DISTINCT_BOATS", + "IGNORES_BLANK_SPECIES", + "IGNORES_ZERO_WEIGHT", + "SUMS_TWO_SPECIES", + "TRIMS_AND_UPPERCASES" + ] + }, + { + "object": "{{P}}FISH_AUCTION", + "mutant": "line 71: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "COUNTS_DISTINCT_BOATS", + "IGNORES_ZERO_WEIGHT", + "SUMS_TWO_SPECIES", + "TRIMS_AND_UPPERCASES" + ] + }, + { + "object": "{{P}}FISH_AUCTION", + "mutant": "line 83: > -> <= (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "BEST_SPECIES_HIGHEST", + "BEST_SPECIES_TIE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1009/reference/fish_auction.clas.abap b/tasks_gen/train/G1009/reference/fish_auction.clas.abap new file mode 100644 index 0000000..323fabc --- /dev/null +++ b/tasks_gen/train/G1009/reference/fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/reference/fish_auction.testclasses.abap b/tasks_gen/train/G1009/reference/fish_auction.testclasses.abap new file mode 100644 index 0000000..07d6ef6 --- /dev/null +++ b/tasks_gen/train/G1009/reference/fish_auction.testclasses.abap @@ -0,0 +1,77 @@ +CLASS ltc_fish_auction DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}fish_auction. + METHODS setup. + METHODS sums_and_counts FOR TESTING. + METHODS ignores_invalid_entries FOR TESTING. + METHODS normalizes_species FOR TESTING. + METHODS result_is_sorted FOR TESTING. + METHODS best_species_wins FOR TESTING. + METHODS best_species_tie FOR TESTING. +ENDCLASS. + + +CLASS ltc_fish_auction IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}fish_auction( ). + ENDMETHOD. + + METHOD sums_and_counts. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 100 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 50 ) + ( boat_id = 'B1' species = 'COD' weight_kg = 25 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 175 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD ignores_invalid_entries. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD normalizes_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' tuna' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA ' weight_kg = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'TUNA'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + ENDMETHOD. + + METHOD result_is_sorted. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 10 ) ) ). + + READ TABLE lt_result INTO DATA(ls_row) INDEX 1. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'COD' ). + ENDMETHOD. + + METHOD best_species_wins. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA' weight_kg = 40 ) ) ) + exp = 'TUNA' ). + ENDMETHOD. + + METHOD best_species_tie. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 30 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 30 ) ) ) + exp = 'COD' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1009/spec.md b/tasks_gen/train/G1009/spec.md new file mode 100644 index 0000000..185ecf1 --- /dev/null +++ b/tasks_gen/train/G1009/spec.md @@ -0,0 +1,74 @@ +# 1. Goal + +A fishing cooperative auctions the catch of its boats. Each boat reports its catch +as a list of entries. The auctioneer needs one consolidated list for each species. +A program calls the class before it prints the auction list. + +# 2. Open questions + +None. + +# 3. Context + +- Package $TMP. +- The class is new. No other object exists for this task. + +# 4. Contract + +Create the class {{P}}FISH_AUCTION in package $TMP. + +- The class is public and final. +- The class has a public constructor without parameters. +- Public types: + - TY_SPECIES: character, length 10. + - TY_BOAT_ID: character, length 10. + - TY_CATCH: structure with the components BOAT_ID (type ty_boat_id), + SPECIES (type ty_species), and WEIGHT_KG (type i). + - TT_CATCH: table type of ty_catch. The table can hold more than one entry + with the same boat ID and the same species. + - TY_SUMMARY: structure with the components SPECIES (type ty_species), + TOTAL_WEIGHT (type i), and BOAT_COUNT (type i). + - TT_SUMMARY: table type of ty_summary. +- Public methods: + - SUMMARIZE, importing IT_CATCH type tt_catch, returning VALUE(RT_SUMMARY) + type tt_summary. + - BEST_SPECIES, importing IT_CATCH type tt_catch, returning VALUE(RV_SPECIES) + type ty_species. +- Do not add other public methods. + +# 5. Business rules + +SUMMARIZE: + +1. Do not change the input table. +2. For each entry, build the species key: remove the leading and the trailing + blanks from SPECIES and convert all letters to upper case. +3. Ignore the entry if the species key is empty. Ignore the entry if WEIGHT_KG + is zero or less. +4. Group the remaining entries by the species key. +5. TOTAL_WEIGHT of a group is the sum of WEIGHT_KG of all entries in the group. +6. BOAT_COUNT of a group is the number of different boat IDs in the group. +7. The result has one row for each group. The rows are in ascending order of the + species key. +8. If no entry remains, the result is empty. + +BEST_SPECIES: + +9. Build the groups with the rules 1 to 8. +10. Return the species key of the group with the highest TOTAL_WEIGHT. +11. If two groups have the same highest TOTAL_WEIGHT, return the species key that + is first in ascending order. +12. If there is no group, return an empty value. + +# 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 create or change 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/G1009/task.json b/tasks_gen/train/G1009/task.json new file mode 100644 index 0000000..bd6fb31 --- /dev/null +++ b/tasks_gen/train/G1009/task.json @@ -0,0 +1,43 @@ +{ + "id": "G1009", + "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}}FISH_AUCTION" + } + ], + "out_of_scope": [ + "No other object in package $TMP." + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}FISH_AUCTION", + "file": "reference/fish_auction.clas.abap", + "description": "Consolidates the catch of the fishing boats", + "testclasses_file": "reference/fish_auction.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1010/faulty/m0_phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/faulty/m0_phone_repair_pricer.clas.abap new file mode 100644 index 0000000..bf35dbb --- /dev/null +++ b/tasks_gen/train/G1010/faulty/m0_phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price / device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/faulty/m1_phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/faulty/m1_phone_repair_pricer.clas.abap new file mode 100644 index 0000000..05453a0 --- /dev/null +++ b/tasks_gen/train/G1010/faulty/m1_phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price >= c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/faulty/m2_phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/faulty/m2_phone_repair_pricer.clas.abap new file mode 100644 index 0000000..beb56e9 --- /dev/null +++ b/tasks_gen/train/G1010/faulty/m2_phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express <> abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/faulty/m3_phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/faulty/m3_phone_repair_pricer.clas.abap new file mode 100644 index 0000000..0a2dc24 --- /dev/null +++ b/tasks_gen/train/G1010/faulty/m3_phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_false. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/faulty/m4_phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/faulty/m4_phone_repair_pricer.clas.abap new file mode 100644 index 0000000..27a7585 --- /dev/null +++ b/tasks_gen/train/G1010/faulty/m4_phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price / c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/generation.json b/tasks_gen/train/G1010/generation.json new file mode 100644 index 0000000..766219e --- /dev/null +++ b/tasks_gen/train/G1010/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1010", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/phone_repair_pricer.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 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/G1010/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1010/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..59c120d --- /dev/null +++ b/tasks_gen/train/G1010/hidden/t31_hidden.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_screen_basic TYPE ty_price VALUE '89.00'. + CONSTANTS c_high_full TYPE ty_price VALUE '101.00'. + CONSTANTS c_mid_board TYPE ty_price VALUE '163.90'. + CONSTANTS c_minimum TYPE ty_price VALUE '25.00'. + CONSTANTS c_zero TYPE ty_price VALUE '0.00'. + CONSTANTS c_port_basic TYPE ty_price VALUE '49.00'. + CONSTANTS c_camera_express TYPE ty_price VALUE '74.00'. + CONSTANTS c_camera_full TYPE ty_price VALUE '59.20'. + CONSTANTS c_port_high TYPE ty_price VALUE '61.25'. + + DATA mo_cut TYPE REF TO {{p}}phone_repair_pricer. + + METHODS setup. + METHODS screen_basic_plain FOR TESTING. + METHODS screen_high_express_warranty FOR TESTING. + METHODS mid_device_surcharge FOR TESTING. + METHODS battery_minimum_charge FOR TESTING. + METHODS battery_warranty_minimum FOR TESTING. + METHODS unknown_repair_is_zero FOR TESTING. + METHODS unknown_repair_ignores_extras FOR TESTING. + METHODS unknown_device_is_basic FOR TESTING. + METHODS express_adds_fee FOR TESTING. + METHODS warranty_after_express FOR TESTING. + METHODS port_high_no_extras FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}phone_repair_pricer( ). + ENDMETHOD. + + METHOD screen_basic_plain. + cl_abap_unit_assert=>assert_equals( + exp = c_screen_basic + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SCREEN' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD screen_high_express_warranty. + cl_abap_unit_assert=>assert_equals( + exp = c_high_full + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SCREEN' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD mid_device_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_mid_board + act = mo_cut->calculate_price( iv_device = 'MID' + iv_repair = 'BOARD' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD battery_minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD battery_warranty_minimum. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD unknown_repair_is_zero. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SPEAKER' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD unknown_repair_ignores_extras. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SPEAKER' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD unknown_device_is_basic. + cl_abap_unit_assert=>assert_equals( + exp = c_port_basic + act = mo_cut->calculate_price( iv_device = 'ULTRA' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD express_adds_fee. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_express + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD warranty_after_express. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_full + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD port_high_no_extras. + cl_abap_unit_assert=>assert_equals( + exp = c_port_high + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/mutation.json b/tasks_gen/train/G1010/mutation.json new file mode 100644 index 0000000..f5d1209 --- /dev/null +++ b/tasks_gen/train/G1010/mutation.json @@ -0,0 +1,79 @@ +{ + "task": "G1010", + "mutants": [ + { + "object": "{{P}}PHONE_REPAIR_PRICER", + "mutant": "line 60: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "MID_DEVICE_SURCHARGE", + "PORT_HIGH_NO_EXTRAS", + "SCREEN_HIGH_EXPRESS_WARRANTY" + ] + }, + { + "object": "{{P}}PHONE_REPAIR_PRICER", + "mutant": "line 65: < -> >= (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BATTERY_MINIMUM_CHARGE", + "BATTERY_WARRANTY_MINIMUM", + "EXPRESS_ADDS_FEE", + "MID_DEVICE_SURCHARGE", + "PORT_HIGH_NO_EXTRAS", + "SCREEN_BASIC_PLAIN", + "SCREEN_HIGH_EXPRESS_WARRANTY", + "UNKNOWN_DEVICE_IS_BASIC", + "WARRANTY_AFTER_EXPRESS" + ] + }, + { + "object": "{{P}}PHONE_REPAIR_PRICER", + "mutant": "line 98: = -> <> (eq)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "BATTERY_MINIMUM_CHARGE", + "BATTERY_WARRANTY_MINIMUM", + "EXPRESS_ADDS_FEE", + "MID_DEVICE_SURCHARGE", + "PORT_HIGH_NO_EXTRAS", + "SCREEN_BASIC_PLAIN", + "SCREEN_HIGH_EXPRESS_WARRANTY", + "UNKNOWN_DEVICE_IS_BASIC", + "WARRANTY_AFTER_EXPRESS" + ] + }, + { + "object": "{{P}}PHONE_REPAIR_PRICER", + "mutant": "line 101: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "EXPRESS_ADDS_FEE", + "MID_DEVICE_SURCHARGE", + "PORT_HIGH_NO_EXTRAS", + "SCREEN_BASIC_PLAIN", + "SCREEN_HIGH_EXPRESS_WARRANTY", + "UNKNOWN_DEVICE_IS_BASIC", + "WARRANTY_AFTER_EXPRESS" + ] + }, + { + "object": "{{P}}PHONE_REPAIR_PRICER", + "mutant": "line 102: * -> / (arith)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "SCREEN_HIGH_EXPRESS_WARRANTY", + "WARRANTY_AFTER_EXPRESS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1010/reference/phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/reference/phone_repair_pricer.clas.abap new file mode 100644 index 0000000..c7a38ad --- /dev/null +++ b/tasks_gen/train/G1010/reference/phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/reference/phone_repair_pricer.testclasses.abap b/tasks_gen/train/G1010/reference/phone_repair_pricer.testclasses.abap new file mode 100644 index 0000000..b4393e6 --- /dev/null +++ b/tasks_gen/train/G1010/reference/phone_repair_pricer.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_phone_repair_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_screen_basic TYPE ty_price VALUE '89.00'. + CONSTANTS c_port_high TYPE ty_price VALUE '61.25'. + CONSTANTS c_camera_express TYPE ty_price VALUE '74.00'. + CONSTANTS c_camera_full TYPE ty_price VALUE '59.20'. + CONSTANTS c_minimum TYPE ty_price VALUE '25.00'. + CONSTANTS c_zero TYPE ty_price VALUE '0.00'. + + DATA mo_cut TYPE REF TO {{p}}phone_repair_pricer. + + METHODS setup. + METHODS base_price_screen FOR TESTING. + METHODS device_factor_high FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS warranty_rate FOR TESTING. + METHODS minimum_price FOR TESTING. + METHODS unknown_repair FOR TESTING. +ENDCLASS. + + +CLASS ltc_phone_repair_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}phone_repair_pricer( ). + ENDMETHOD. + + METHOD base_price_screen. + cl_abap_unit_assert=>assert_equals( + exp = c_screen_basic + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SCREEN' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD device_factor_high. + cl_abap_unit_assert=>assert_equals( + exp = c_port_high + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_express + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD warranty_rate. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_full + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD minimum_price. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD unknown_repair. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SPEAKER' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/seed/phone_repair_pricer.clas.abap b/tasks_gen/train/G1010/seed/phone_repair_pricer.clas.abap new file mode 100644 index 0000000..9efe690 --- /dev/null +++ b/tasks_gen/train/G1010/seed/phone_repair_pricer.clas.abap @@ -0,0 +1,90 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + DATA lv_price TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_found TYPE abap_bool. + + CLEAR lv_found. + IF iv_repair = c_repair_screen. + lv_price = '89.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_battery. + lv_price = '19.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_board. + lv_price = '149.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_camera. + lv_price = '59.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_port. + lv_price = '49.00'. + lv_found = abap_true. + ENDIF. + + IF lv_found = abap_true. + IF iv_device = c_device_high. + lv_factor = '1.25'. + lv_price = lv_price * lv_factor. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ELSEIF iv_device = c_device_mid. + lv_factor = '1.10'. + lv_price = lv_price * lv_factor. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ELSE. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ENDIF. + + IF lv_price < 25. + lv_price = 25. + ENDIF. + ELSE. + CLEAR lv_price. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1010/spec.md b/tasks_gen/train/G1010/spec.md new file mode 100644 index 0000000..01ed752 --- /dev/null +++ b/tasks_gen/train/G1010/spec.md @@ -0,0 +1,55 @@ +# 1. Goal +A mobile phone repair shop prices repair orders. The checkout program of the +shop calls the class {{P}}PHONE_REPAIR_PRICER for each order and prints the +price that the class returns. The class works, but its code is legacy code: one +long method, repeated blocks and prices that are hidden in the code. Refactor +the class in place. The behavior must stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}PHONE_REPAIR_PRICER exists in package $TMP and is active. Its + source code is the legacy code. +- The checkout program calls the public method CALCULATE_PRICE. It passes the + device class, the repair type, the express flag and the warranty flag, and it + prints the returned price. +- The class is in production. Its public interface must not change. +- The class has no tests. + +# 4. Contract +- Refactor the class {{P}}PHONE_REPAIR_PRICER in package $TMP. Keep the name. + Do not create a second class. +- The class stays public and final. The constructor stays public and has no + parameters. +- The public section stays as it is: + - the type TY_DEVICE (character 6), the type TY_REPAIR (character 8) and the + type TY_PRICE (packed number, length 8, 2 decimals); + - the constants C_DEVICE_BASIC, C_DEVICE_MID and C_DEVICE_HIGH of type + TY_DEVICE; + - the constants C_REPAIR_SCREEN, C_REPAIR_BATTERY, C_REPAIR_BOARD, + C_REPAIR_CAMERA and C_REPAIR_PORT of type TY_REPAIR; + - the method CALCULATE_PRICE with the importing parameters IV_DEVICE + (TY_DEVICE), IV_REPAIR (TY_REPAIR), IV_EXPRESS (ABAP_BOOL) and IV_WARRANTY + (ABAP_BOOL), and the returning parameter RV_PRICE (TY_PRICE). +- Do not add other public methods, types, constants or attributes. + +# 5. Business rules +- The behavior of the class must not change. Every combination of the four + input parameters must return the same price as before. +- The rules are in the legacy source code of the class. Read the legacy code. + Keep every rule and keep the sequence of the steps. +- Do not add rules and do not remove rules. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No comment that restates the code. Replace the prices and the + factors with named constants. Remove the repeated blocks. Use names that say + what the data and the methods mean. +- Out of scope: do not change the public interface of the class. + +# 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/G1010/task.json b/tasks_gen/train/G1010/task.json new file mode 100644 index 0000000..f854055 --- /dev/null +++ b/tasks_gen/train/G1010/task.json @@ -0,0 +1,49 @@ +{ + "id": "G1010", + "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}}PHONE_REPAIR_PRICER", + "file": "seed/phone_repair_pricer.clas.abap", + "description": "Legacy phone repair price calculator: one long method, repeated blocks, prices hidden in the code" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PHONE_REPAIR_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "Behavior tests for the refactored phone repair price calculator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PHONE_REPAIR_PRICER", + "file": "reference/phone_repair_pricer.clas.abap", + "description": "Refactored phone repair price calculator", + "testclasses_file": "reference/phone_repair_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "duplicate_code", + "magic_numbers" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1011/faulty/m1_i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/faulty/m1_i_repair_charge.ddls.asddls new file mode 100644 index 0000000..123bbc6 --- /dev/null +++ b/tasks_gen/train/G1011/faulty/m1_i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + or rate.repair_type = job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/faulty/m2_i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/faulty/m2_i_repair_charge.ddls.asddls new file mode 100644 index 0000000..da0baab --- /dev/null +++ b/tasks_gen/train/G1011/faulty/m2_i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + and rate.repair_type <> job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/faulty/m3_i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/faulty/m3_i_repair_charge.ddls.asddls new file mode 100644 index 0000000..13a4dd0 --- /dev/null +++ b/tasks_gen/train/G1011/faulty/m3_i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + and rate.repair_type = job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + max( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/faulty/m4_i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/faulty/m4_i_repair_charge.ddls.asddls new file mode 100644 index 0000000..dcf728f --- /dev/null +++ b/tasks_gen/train/G1011/faulty/m4_i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + and rate.repair_type = job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state <> 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/generation.json b/tasks_gen/train/G1011/generation.json new file mode 100644 index 0000000..5ab43db --- /dev/null +++ b/tasks_gen/train/G1011/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1011", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "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/G1011/hidden/repair_charge_hidden.clas.abap b/tasks_gen/train/G1011/hidden/repair_charge_hidden.clas.abap new file mode 100644 index 0000000..c743943 --- /dev/null +++ b/tasks_gen/train/G1011/hidden/repair_charge_hidden.clas.abap @@ -0,0 +1,97 @@ +CLASS {{p}}repair_charge_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 charge_c1 FOR TESTING. + METHODS charge_c2 FOR TESTING. + METHODS count_c1 FOR TESTING. + METHODS count_c2 FOR TESTING. + METHODS pairs_c1 FOR TESTING. + METHODS pairs_c2 FOR TESTING. + METHODS single_rate_ok FOR TESTING. + METHODS open_job_hidden FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}repair_charge_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_REPAIR_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_job = VALUE #( + ( job_id = 'J1' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'J2' customer = 'C1' shoe_type = 'SHOE' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J3' customer = 'C2' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 3 job_state = 'D' ) + ( job_id = 'J4' customer = 'C3' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 1 job_state = 'O' ) + ( job_id = 'J5' customer = 'C2' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J6' customer = 'C4' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 2 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '12.50' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '6.25' ) + ( shoe_type = 'SHOE' repair_type = 'HEEL' price = '4.00' ) + ( shoe_type = 'SHOE' repair_type = 'SOLE' price = '3.75' ) + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.00' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + ENDMETHOD. + + METHOD charge_c1. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '29.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_c2. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '43.75' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD count_c1. + SELECT SINGLE job_count FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD count_c2. + SELECT SINGLE job_count FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD pairs_c1. + SELECT SINGLE total_pairs FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_pairs). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_pairs ). + ENDMETHOD. + + METHOD pairs_c2. + SELECT SINGLE total_pairs FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_pairs). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_pairs ). + ENDMETHOD. + + METHOD single_rate_ok. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C4' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD open_job_hidden. + SELECT COUNT(*) FROM {{p}}i_repair_charge WHERE customer = 'C3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_repair_charge INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1011/mutation.json b/tasks_gen/train/G1011/mutation.json new file mode 100644 index 0000000..c983dd3 --- /dev/null +++ b/tasks_gen/train/G1011/mutation.json @@ -0,0 +1,69 @@ +{ + "task": "G1011", + "mutants": [ + { + "object": "{{P}}I_REPAIR_CHARGE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "9/9", + "failed_tests": [] + }, + { + "object": "{{P}}I_REPAIR_CHARGE", + "mutant": "line 6: and -> or (logic)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "CHARGE_C1", + "CHARGE_C2", + "COUNT_C1", + "COUNT_C2", + "PAIRS_C1", + "PAIRS_C2" + ] + }, + { + "object": "{{P}}I_REPAIR_CHARGE", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "5/9", + "failed_tests": [ + "CHARGE_C1", + "CHARGE_C2", + "ROW_COUNT", + "SINGLE_RATE_OK" + ] + }, + { + "object": "{{P}}I_REPAIR_CHARGE", + "mutant": "line 11: sum( -> max( (agg)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "CHARGE_C1", + "CHARGE_C2" + ] + }, + { + "object": "{{P}}I_REPAIR_CHARGE", + "mutant": "line 13: = -> <> (eq)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "CHARGE_C1", + "CHARGE_C2", + "COUNT_C1", + "COUNT_C2", + "OPEN_JOB_HIDDEN", + "PAIRS_C1", + "PAIRS_C2", + "ROW_COUNT", + "SINGLE_RATE_OK" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1011/reference/i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/reference/i_repair_charge.ddls.asddls new file mode 100644 index 0000000..05fd992 --- /dev/null +++ b/tasks_gen/train/G1011/reference/i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + and rate.repair_type = job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/reference/repair_charge_test.clas.abap b/tasks_gen/train/G1011/reference/repair_charge_test.clas.abap new file mode 100644 index 0000000..dfd4146 --- /dev/null +++ b/tasks_gen/train/G1011/reference/repair_charge_test.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}repair_charge_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 charge_of_two_jobs FOR TESTING. + METHODS charge_of_single_rate FOR TESTING. +ENDCLASS. + + +CLASS {{p}}repair_charge_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_REPAIR_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD charge_of_two_jobs. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + lt_job = VALUE #( + ( job_id = 'A' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'B' customer = 'C1' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '10.00' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '5.00' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + SELECT SINGLE job_count, total_pairs, total_charge FROM {{p}}i_repair_charge + WHERE customer = 'C1' INTO @DATA(ls_charge). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_charge-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_charge-total_pairs ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.00' ) + act = CONV decfloat34( ls_charge-total_charge ) ). + ENDMETHOD. + + METHOD charge_of_single_rate. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + lt_job = VALUE #( + ( job_id = 'C' customer = 'C2' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 4 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.50' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + SELECT SINGLE total_charge FROM {{p}}i_repair_charge + WHERE customer = 'C2' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '22.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1011/seed/i_repair_charge.ddls.asddls b/tasks_gen/train/G1011/seed/i_repair_charge.ddls.asddls new file mode 100644 index 0000000..88b1d59 --- /dev/null +++ b/tasks_gen/train/G1011/seed/i_repair_charge.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1011/seed/job.tabl.asabap b/tasks_gen/train/G1011/seed/job.tabl.asabap new file mode 100644 index 0000000..88f9725 --- /dev/null +++ b/tasks_gen/train/G1011/seed/job.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Repair job' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}job { + key client : abap.clnt not null; + key job_id : abap.char(10) not null; + customer : abap.char(10); + shoe_type : abap.char(4); + repair_type : abap.char(4); + pairs : abap.int4; + job_state : abap.char(1); +} diff --git a/tasks_gen/train/G1011/seed/rate.tabl.asabap b/tasks_gen/train/G1011/seed/rate.tabl.asabap new file mode 100644 index 0000000..a4cc904 --- /dev/null +++ b/tasks_gen/train/G1011/seed/rate.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Repair rate' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}rate { + key client : abap.clnt not null; + key shoe_type : abap.char(4) not null; + key repair_type : abap.char(4) not null; + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1011/seed/seed_repair.clas.abap b/tasks_gen/train/G1011/seed/seed_repair.clas.abap new file mode 100644 index 0000000..bb290a3 --- /dev/null +++ b/tasks_gen/train/G1011/seed/seed_repair.clas.abap @@ -0,0 +1,34 @@ +CLASS {{p}}seed_repair DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_repair IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + + lt_job = VALUE #( + ( job_id = 'J1' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'J2' customer = 'C1' shoe_type = 'SHOE' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J3' customer = 'C2' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 3 job_state = 'D' ) + ( job_id = 'J4' customer = 'C3' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 1 job_state = 'O' ) + ( job_id = 'J5' customer = 'C2' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J6' customer = 'C4' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 2 job_state = 'D' ) ). + + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '12.50' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '6.25' ) + ( shoe_type = 'SHOE' repair_type = 'HEEL' price = '4.00' ) + ( shoe_type = 'SHOE' repair_type = 'SOLE' price = '3.75' ) + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.00' ) ). + + DELETE FROM {{p}}job. + DELETE FROM {{p}}rate. + INSERT {{p}}job FROM TABLE @lt_job. + INSERT {{p}}rate FROM TABLE @lt_rate. + + out->write( |Jobs: { lines( lt_job ) }, rates: { lines( lt_rate ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1011/spec.md b/tasks_gen/train/G1011/spec.md new file mode 100644 index 0000000..40160ec --- /dev/null +++ b/tasks_gen/train/G1011/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Give the total repair charge of each customer of a cobbler's workshop. A report +and an OData service read the charges, so the calculation must be in the +database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}JOB (repair job), package $TMP. Key: JOB_ID (CHAR 10). + Fields: CUSTOMER (CHAR 10), SHOE_TYPE (CHAR 4), REPAIR_TYPE (CHAR 4), + PAIRS (INT4), JOB_STATE (CHAR 1). +- Table {{P}}RATE (repair rate), package $TMP. Key: SHOE_TYPE (CHAR 4), + REPAIR_TYPE (CHAR 4). Field: PRICE (DEC 15,2, price for one pair). +- The CDS view entity {{P}}I_REPAIR_CHARGE in package $TMP already exists and + has a defect. + +# 4. Contract +- Fix the CDS view entity {{P}}I_REPAIR_CHARGE in package $TMP. +- Elements, with these names: CUSTOMER (key), JOB_COUNT, TOTAL_PAIRS, + TOTAL_CHARGE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each customer that has at least one repair job + in the view. +2. JOB_COUNT is the number of repair jobs of the customer in the view. +3. TOTAL_PAIRS is the sum of PAIRS of these jobs. +4. The charge of a repair job is its PAIRS multiplied by the price that the + rate table holds for the shoe type and the repair type of the job. + TOTAL_CHARGE is the sum of the charges of these jobs, with 2 decimals. +5. Users report that JOB_COUNT, TOTAL_PAIRS and TOTAL_CHARGE of a customer are + too high. The three values are correct when the customer has no job whose + shoe type has more than one repair type in the rate table. The three values + must be correct for every customer. +6. All other behavior of the view stays the same. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. No ATC priority 1 or 2 findings. +- Out of scope: do not change {{P}}JOB and {{P}}RATE. No dynpro, no + SmartForms, no BAdI, no RAP behavior definition. + +# 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/G1011/task.json b/tasks_gen/train/G1011/task.json new file mode 100644 index 0000000..d9e4c1f --- /dev/null +++ b/tasks_gen/train/G1011/task.json @@ -0,0 +1,78 @@ +{ + "id": "G1011", + "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}}JOB", + "file": "seed/job.tabl.asabap", + "description": "Repair job" + }, + { + "type": "TABL", + "name": "{{P}}RATE", + "file": "seed/rate.tabl.asabap", + "description": "Repair rate" + }, + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "file": "seed/i_repair_charge.ddls.asddls", + "description": "Faulty repair charge view" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_REPAIR", + "file": "seed/seed_repair.clas.abap", + "description": "Seed data for the repair tables", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "fields": [ + "CUSTOMER", + "JOB_COUNT", + "TOTAL_PAIRS", + "TOTAL_CHARGE" + ] + } + ], + "out_of_scope": [ + "{{P}}JOB", + "{{P}}RATE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}REPAIR_CHARGE_HIDDEN", + "file": "hidden/repair_charge_hidden.clas.abap", + "description": "Hidden tests for the repair charge view" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "file": "reference/i_repair_charge.ddls.asddls", + "description": "Fixed repair charge view" + }, + { + "type": "CLAS", + "name": "{{P}}REPAIR_CHARGE_TEST", + "file": "reference/repair_charge_test.clas.abap", + "description": "Own tests for the repair charge view" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1012/faulty/m0_i_milk_total.ddls.asddls b/tasks_gen/train/G1012/faulty/m0_i_milk_total.ddls.asddls new file mode 100644 index 0000000..2fae156 --- /dev/null +++ b/tasks_gen/train/G1012/faulty/m0_i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + left outer join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) > 500 diff --git a/tasks_gen/train/G1012/faulty/m1_i_milk_total.ddls.asddls b/tasks_gen/train/G1012/faulty/m1_i_milk_total.ddls.asddls new file mode 100644 index 0000000..d512e52 --- /dev/null +++ b/tasks_gen/train/G1012/faulty/m1_i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + max( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) > 500 diff --git a/tasks_gen/train/G1012/faulty/m2_i_milk_total.ddls.asddls b/tasks_gen/train/G1012/faulty/m2_i_milk_total.ddls.asddls new file mode 100644 index 0000000..f8c3611 --- /dev/null +++ b/tasks_gen/train/G1012/faulty/m2_i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + max( milk.litres ) > 500 diff --git a/tasks_gen/train/G1012/faulty/m3_i_milk_total.ddls.asddls b/tasks_gen/train/G1012/faulty/m3_i_milk_total.ddls.asddls new file mode 100644 index 0000000..49b9c5c --- /dev/null +++ b/tasks_gen/train/G1012/faulty/m3_i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) <= 500 diff --git a/tasks_gen/train/G1012/faulty/m4_i_milk_total.ddls.asddls b/tasks_gen/train/G1012/faulty/m4_i_milk_total.ddls.asddls new file mode 100644 index 0000000..c993924 --- /dev/null +++ b/tasks_gen/train/G1012/faulty/m4_i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) > 501 diff --git a/tasks_gen/train/G1012/generation.json b/tasks_gen/train/G1012/generation.json new file mode 100644 index 0000000..5158377 --- /dev/null +++ b/tasks_gen/train/G1012/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1012", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "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/G1012/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1012/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..81822ea --- /dev/null +++ b/tasks_gen/train/G1012/hidden/t16_hidden.clas.abap @@ -0,0 +1,99 @@ +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 only_farms_above_limit FOR TESTING. + METHODS total_litres FOR TESTING. + METHODS delivery_count FOR TESTING. + METHODS farm_below_limit_hidden FOR TESTING. + METHODS farm_at_limit_hidden FOR TESTING. + METHODS farm_just_above_limit FOR TESTING. + METHODS no_deliv_farm_hidden FOR TESTING. + METHODS unknown_farm_hidden FOR TESTING. + METHODS farmer_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MILK_TOTAL' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_farm = VALUE #( ( farm_id = 'F1' farmer = 'ALPHA' region = 'NORTH' ) + ( farm_id = 'F2' farmer = 'BETA' region = 'NORTH' ) + ( farm_id = 'F3' farmer = 'GAMMA' region = 'SOUTH' ) + ( farm_id = 'F4' farmer = 'DELTA' region = 'SOUTH' ) + ( farm_id = 'F5' farmer = 'EPSILON' region = 'EAST' ) ). + lt_milk = VALUE #( ( farm_id = 'F1' deliv_date = '20240101' litres = '300.0' ) + ( farm_id = 'F1' deliv_date = '20240102' litres = '250.5' ) + ( farm_id = 'F2' deliv_date = '20240101' litres = '200.0' ) + ( farm_id = 'F2' deliv_date = '20240102' litres = '100.0' ) + ( farm_id = 'F3' deliv_date = '20240101' litres = '500.0' ) + ( farm_id = 'F5' deliv_date = '20240101' litres = '250.0' ) + ( farm_id = 'F5' deliv_date = '20240102' litres = '250.5' ) + ( farm_id = 'X9' deliv_date = '20240101' litres = '600.0' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + ENDMETHOD. + + METHOD only_farms_above_limit. + SELECT COUNT(*) FROM {{p}}i_milk_total INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD total_litres. + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'F1' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '550.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD delivery_count. + SELECT SINGLE delivery_count FROM {{p}}i_milk_total WHERE farm_id = 'F1' + INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD farm_below_limit_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_at_limit_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_just_above_limit. + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'F5' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '500.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD no_deliv_farm_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD unknown_farm_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'X9' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farmer_shown. + SELECT SINGLE farmer FROM {{p}}i_milk_total WHERE farm_id = 'F1' INTO @DATA(lv_farmer). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lv_farmer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1012/mutation.json b/tasks_gen/train/G1012/mutation.json new file mode 100644 index 0000000..8a3b6a8 --- /dev/null +++ b/tasks_gen/train/G1012/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G1012", + "mutants": [ + { + "object": "{{P}}I_MILK_TOTAL", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "ONLY_FARMS_ABOVE_LIMIT", + "UNKNOWN_FARM_HIDDEN" + ] + }, + { + "object": "{{P}}I_MILK_TOTAL", + "mutant": "line 9: sum( -> max( (agg)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "FARM_JUST_ABOVE_LIMIT", + "TOTAL_LITRES" + ] + }, + { + "object": "{{P}}I_MILK_TOTAL", + "mutant": "line 16: sum( -> max( (agg)", + "status": "killed", + "hidden": "4/9", + "failed_tests": [ + "DELIVERY_COUNT", + "FARMER_SHOWN", + "FARM_JUST_ABOVE_LIMIT", + "ONLY_FARMS_ABOVE_LIMIT", + "TOTAL_LITRES" + ] + }, + { + "object": "{{P}}I_MILK_TOTAL", + "mutant": "line 16: > -> <= (rel)", + "status": "killed", + "hidden": "3/9", + "failed_tests": [ + "DELIVERY_COUNT", + "FARMER_SHOWN", + "FARM_AT_LIMIT_HIDDEN", + "FARM_BELOW_LIMIT_HIDDEN", + "FARM_JUST_ABOVE_LIMIT", + "TOTAL_LITRES" + ] + }, + { + "object": "{{P}}I_MILK_TOTAL", + "mutant": "line 16: 500 -> 501 (const)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "FARM_JUST_ABOVE_LIMIT", + "ONLY_FARMS_ABOVE_LIMIT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1012/reference/i_milk_total.ddls.asddls b/tasks_gen/train/G1012/reference/i_milk_total.ddls.asddls new file mode 100644 index 0000000..4b1954c --- /dev/null +++ b/tasks_gen/train/G1012/reference/i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) > 500 diff --git a/tasks_gen/train/G1012/reference/t16_test.clas.abap b/tasks_gen/train/G1012/reference/t16_test.clas.abap new file mode 100644 index 0000000..3cdbaf5 --- /dev/null +++ b/tasks_gen/train/G1012/reference/t16_test.clas.abap @@ -0,0 +1,66 @@ +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 total_of_one_farm FOR TESTING. + METHODS farm_below_limit FOR TESTING. + METHODS farm_just_above_limit FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MILK_TOTAL' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD total_of_one_farm. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'A' farmer = 'ANN' region = 'EAST' ) ). + lt_milk = VALUE #( ( farm_id = 'A' deliv_date = '20240101' litres = '400.0' ) + ( farm_id = 'A' deliv_date = '20240102' litres = '150.5' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT SINGLE total_litres, delivery_count FROM {{p}}i_milk_total WHERE farm_id = 'A' + INTO @DATA(ls_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '550.5' ) + act = CONV decfloat34( ls_total-total_litres ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_total-delivery_count ). + ENDMETHOD. + + METHOD farm_below_limit. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'B' farmer = 'BOB' region = 'WEST' ) ). + lt_milk = VALUE #( ( farm_id = 'B' deliv_date = '20240101' litres = '100.0' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT COUNT(*) FROM {{p}}i_milk_total INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_just_above_limit. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'C' farmer = 'CARL' region = 'WEST' ) ). + lt_milk = VALUE #( ( farm_id = 'C' deliv_date = '20240101' litres = '250.0' ) + ( farm_id = 'C' deliv_date = '20240102' litres = '250.5' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'C' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '500.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1012/seed/farm.tabl.asabap b/tasks_gen/train/G1012/seed/farm.tabl.asabap new file mode 100644 index 0000000..35baf34 --- /dev/null +++ b/tasks_gen/train/G1012/seed/farm.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Farm master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}farm { + key client : abap.clnt not null; + key farm_id : abap.char(10) not null; + farmer : abap.char(20); + region : abap.char(10); +} diff --git a/tasks_gen/train/G1012/seed/milk.tabl.asabap b/tasks_gen/train/G1012/seed/milk.tabl.asabap new file mode 100644 index 0000000..7553baa --- /dev/null +++ b/tasks_gen/train/G1012/seed/milk.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Milk delivery' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}milk { + key client : abap.clnt not null; + key farm_id : abap.char(10) not null; + key deliv_date : abap.dats not null; + litres : abap.dec(10,1); +} diff --git a/tasks_gen/train/G1012/spec.md b/tasks_gen/train/G1012/spec.md new file mode 100644 index 0000000..d468aae --- /dev/null +++ b/tasks_gen/train/G1012/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +Show the milk that each farm delivered. The cooperative wants only the farms +that delivered more than 500 litres in total. A report and an OData service +will read the result, so the calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}FARM (farm master), package $TMP. Key: FARM_ID (CHAR 10). + Fields: FARMER (CHAR 20), REGION (CHAR 10). +- Table {{P}}MILK (milk delivery), package $TMP. Key: FARM_ID (CHAR 10), + DELIV_DATE (DATS). Fields: LITRES (DEC 10,1). + +# 4. Contract +- Create the CDS view entity {{P}}I_MILK_TOTAL in package $TMP. +- Elements, with these names: FARM_ID (key), FARMER, TOTAL_LITRES, + DELIVERY_COUNT. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each farm whose total litres are more than 500. A farm + with a total of 500 litres or less must not be shown. A farm without a + delivery must not be shown. A delivery whose farm is not in the farm + master must not be shown. +2. TOTAL_LITRES is the sum of LITRES of all deliveries of the farm, with one + decimal. +3. DELIVERY_COUNT is the number of deliveries of the farm. +4. FARMER is the farmer of the farm. +5. The rows are grouped by farm. There is exactly one row for each farm. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816). +- Coding standards: Clean ABAP. No ATC priority 1 or 2 findings. +- Out of scope: do not change {{P}}FARM and {{P}}MILK. + +# 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/G1012/task.json b/tasks_gen/train/G1012/task.json new file mode 100644 index 0000000..afc7197 --- /dev/null +++ b/tasks_gen/train/G1012/task.json @@ -0,0 +1,65 @@ +{ + "id": "G1012", + "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}}FARM", + "file": "seed/farm.tabl.asabap", + "description": "Farm master" + }, + { + "type": "TABL", + "name": "{{P}}MILK", + "file": "seed/milk.tabl.asabap", + "description": "Milk delivery" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_MILK_TOTAL", + "fields": [ + "FARM_ID", + "FARMER", + "TOTAL_LITRES", + "DELIVERY_COUNT" + ] + } + ], + "out_of_scope": [ + "{{P}}FARM", + "{{P}}MILK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_MILK_TOTAL", + "file": "reference/i_milk_total.ddls.asddls", + "description": "Milk totals per farm" + }, + { + "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/G1013/faulty/m0_log_merger.clas.abap b/tasks_gen/train/G1013/faulty/m0_log_merger.clas.abap new file mode 100644 index 0000000..4a16f1b --- /dev/null +++ b/tasks_gen/train/G1013/faulty/m0_log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) <> abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/faulty/m1_log_merger.clas.abap b/tasks_gen/train/G1013/faulty/m1_log_merger.clas.abap new file mode 100644 index 0000000..53b027c --- /dev/null +++ b/tasks_gen/train/G1013/faulty/m1_log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_false. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/faulty/m2_log_merger.clas.abap b/tasks_gen/train/G1013/faulty/m2_log_merger.clas.abap new file mode 100644 index 0000000..4adbb53 --- /dev/null +++ b/tasks_gen/train/G1013/faulty/m2_log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 2 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/faulty/m3_log_merger.clas.abap b/tasks_gen/train/G1013/faulty/m3_log_merger.clas.abap new file mode 100644 index 0000000..c9de78f --- /dev/null +++ b/tasks_gen/train/G1013/faulty/m3_log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm < c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/faulty/m4_log_merger.clas.abap b/tasks_gen/train/G1013/faulty/m4_log_merger.clas.abap new file mode 100644 index 0000000..fed7e94 --- /dev/null +++ b/tasks_gen/train/G1013/faulty/m4_log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + OR is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/generation.json b/tasks_gen/train/G1013/generation.json new file mode 100644 index 0000000..1554828 --- /dev/null +++ b/tasks_gen/train/G1013/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1013", + "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/G1013/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1013/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..c98537f --- /dev/null +++ b/tasks_gen/train/G1013/hidden/t20_hidden.clas.abap @@ -0,0 +1,138 @@ +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}}log_merger. + DATA mt_empty TYPE {{p}}log_merger=>tt_log. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS first_wins_on_duplicate FOR TESTING. + METHODS first_wins_within_list FOR TESTING. + METHODS sorts_by_log_no FOR TESTING. + METHODS drops_short_logs FOR TESTING. + METHODS drops_thin_logs FOR TESTING. + METHODS filter_after_dedup FOR TESTING. + METHODS empty_inputs FOR TESTING. + METHODS one_empty_list FOR TESTING. + METHODS counts_by_district FOR TESTING. + METHODS counts_empty_list FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}log_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 600 diameter_cm = 40 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_result[ 2 ]-district ). + ENDMETHOD. + + METHOD first_wins_on_duplicate. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = lt_result[ 1 ]-diameter_cm ). + ENDMETHOD. + + METHOD first_wins_within_list. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000001' district = 'A002' length_cm = 700 diameter_cm = 40 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + ENDMETHOD. + + METHOD sorts_by_log_no. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000003' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 500 diameter_cm = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000003' act = lt_result[ 3 ]-log_no ). + ENDMETHOD. + + METHOD drops_short_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 199 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 200 diameter_cm = 30 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 1 ]-log_no ). + ENDMETHOD. + + METHOD drops_thin_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 19 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 20 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 1 ]-log_no ). + ENDMETHOD. + + METHOD filter_after_dedup. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 100 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD empty_inputs. + DATA(lt_result) = mo_cut->combine( it_first = mt_empty it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD one_empty_list. + DATA(lt_result) = mo_cut->combine( + it_first = mt_empty + it_second = VALUE #( ( log_no = 'LOG0000009' district = 'B001' length_cm = 800 diameter_cm = 45 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000009' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 800 act = lt_result[ 1 ]-length_cm ). + ENDMETHOD. + + METHOD counts_by_district. + DATA(lt_stat) = mo_cut->count_by_district( + it_log = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'B001' length_cm = 300 diameter_cm = 25 ) + ( log_no = 'LOG0000003' district = 'A001' length_cm = 200 diameter_cm = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_stat ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_stat[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_stat[ 1 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_stat[ 1 ]-total_length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_stat[ 2 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_stat[ 2 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_stat[ 2 ]-total_length_cm ). + ENDMETHOD. + + METHOD counts_empty_list. + DATA(lt_stat) = mo_cut->count_by_district( it_log = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_stat ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/mutation.json b/tasks_gen/train/G1013/mutation.json new file mode 100644 index 0000000..d35b3e0 --- /dev/null +++ b/tasks_gen/train/G1013/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1013", + "mutants": [ + { + "object": "{{P}}LOG_MERGER", + "mutant": "line 52: = -> <> (eq)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "DROPS_SHORT_LOGS", + "DROPS_THIN_LOGS", + "FILTER_AFTER_DEDUP", + "FIRST_WINS_ON_DUPLICATE", + "FIRST_WINS_WITHIN_LIST", + "MERGES_TWO_LISTS", + "ONE_EMPTY_LIST", + "SORTS_BY_LOG_NO" + ] + }, + { + "object": "{{P}}LOG_MERGER", + "mutant": "line 52: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "DROPS_SHORT_LOGS", + "DROPS_THIN_LOGS", + "FILTER_AFTER_DEDUP", + "FIRST_WINS_ON_DUPLICATE", + "FIRST_WINS_WITHIN_LIST", + "MERGES_TWO_LISTS", + "ONE_EMPTY_LIST", + "SORTS_BY_LOG_NO" + ] + }, + { + "object": "{{P}}LOG_MERGER", + "mutant": "line 64: 1 -> 2 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "COUNTS_BY_DISTRICT" + ] + }, + { + "object": "{{P}}LOG_MERGER", + "mutant": "line 81: >= -> < (rel)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "DROPS_SHORT_LOGS", + "DROPS_THIN_LOGS", + "FILTER_AFTER_DEDUP", + "FIRST_WINS_ON_DUPLICATE", + "FIRST_WINS_WITHIN_LIST", + "MERGES_TWO_LISTS", + "ONE_EMPTY_LIST", + "SORTS_BY_LOG_NO" + ] + }, + { + "object": "{{P}}LOG_MERGER", + "mutant": "line 82: AND -> OR (logic)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "DROPS_SHORT_LOGS", + "DROPS_THIN_LOGS", + "FILTER_AFTER_DEDUP" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1013/reference/log_merger.clas.abap b/tasks_gen/train/G1013/reference/log_merger.clas.abap new file mode 100644 index 0000000..569cec6 --- /dev/null +++ b/tasks_gen/train/G1013/reference/log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/reference/log_merger.testclasses.abap b/tasks_gen/train/G1013/reference/log_merger.testclasses.abap new file mode 100644 index 0000000..4559836 --- /dev/null +++ b/tasks_gen/train/G1013/reference/log_merger.testclasses.abap @@ -0,0 +1,71 @@ +CLASS ltc_log_merger DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}log_merger. + DATA mt_empty TYPE {{p}}log_merger=>tt_log. + METHODS setup. + METHODS merges_distinct_logs FOR TESTING. + METHODS keeps_first_duplicate FOR TESTING. + METHODS removes_small_logs FOR TESTING. + METHODS sorts_result FOR TESTING. + METHODS empty_input_is_empty FOR TESTING. + METHODS counts_per_district FOR TESTING. +ENDCLASS. + + +CLASS ltc_log_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}log_merger( ). + ENDMETHOD. + + METHOD merges_distinct_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 600 diameter_cm = 40 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + ENDMETHOD. + + METHOD keeps_first_duplicate. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + ENDMETHOD. + + METHOD removes_small_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 150 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 15 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD sorts_result. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 500 diameter_cm = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + ENDMETHOD. + + METHOD empty_input_is_empty. + DATA(lt_result) = mo_cut->combine( it_first = mt_empty it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD counts_per_district. + DATA(lt_stat) = mo_cut->count_by_district( + it_log = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 300 diameter_cm = 25 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_stat ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_stat[ 1 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 800 act = lt_stat[ 1 ]-total_length_cm ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1013/spec.md b/tasks_gen/train/G1013/spec.md new file mode 100644 index 0000000..9376026 --- /dev/null +++ b/tasks_gen/train/G1013/spec.md @@ -0,0 +1,63 @@ +# 1. Goal +A sawmill plans the cutting of logs. The sawmill receives the log lists of two +forest districts. A planning program calls one service that combines the two +lists into one list of logs that are ready for cutting. The program also needs +the number of logs and the total length for each district. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}LOG_MERGER does not exist. Create it in package $TMP. +- A log has a log number, a district, a length in centimetres and a diameter in + centimetres. +- A log number identifies a log. The same log number can occur more than once in + one list and in both lists. +- The caller gives the log lists. The class does not read a database table. + +# 4. Contract +- Create the class {{P}}LOG_MERGER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- Public types of the class: + - TY_LOG_NO: character, length 10. + - TY_DISTRICT: character, length 4. + - TY_LOG: structure with the components LOG_NO (TY_LOG_NO), DISTRICT + (TY_DISTRICT), LENGTH_CM (integer) and DIAMETER_CM (integer). + - TT_LOG: standard table of TY_LOG with an empty key. + - TY_DISTRICT_STAT: structure with the components DISTRICT (TY_DISTRICT), + LOG_QTY (integer) and TOTAL_LENGTH_CM (integer). + - TT_DISTRICT_STAT: standard table of TY_DISTRICT_STAT with an empty key. +- Public methods: + - COMBINE, importing IT_FIRST (TT_LOG) and IT_SECOND (TT_LOG), returning + RT_RESULT (TT_LOG). + - COUNT_BY_DISTRICT, importing IT_LOG (TT_LOG), returning RT_STAT + (TT_DISTRICT_STAT). +- Do not add other public methods. + +# 5. Business rules +COMBINE: +1. Build a merged list. Read IT_FIRST first and IT_SECOND second. Add a log to + the merged list only if its log number is not yet in the merged list. Ignore + every later log with the same log number. +2. Remove every log from the merged list with LENGTH_CM below 200 or with + DIAMETER_CM below 20. +3. Sort the result by LOG_NO in ascending order. +4. The result has no two logs with the same log number. + +COUNT_BY_DISTRICT: +1. The result has one row for each district that occurs in IT_LOG. +2. LOG_QTY is the number of logs of the district. +3. TOTAL_LENGTH_CM is the sum of LENGTH_CM of the logs of the district. +4. Sort the result by DISTRICT in ascending order. +5. An empty input gives an empty result. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: 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/G1013/task.json b/tasks_gen/train/G1013/task.json new file mode 100644 index 0000000..393e28d --- /dev/null +++ b/tasks_gen/train/G1013/task.json @@ -0,0 +1,42 @@ +{ + "id": "G1013", + "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}}LOG_MERGER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "Hidden tests for {{P}}LOG_MERGER" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}LOG_MERGER", + "file": "reference/log_merger.clas.abap", + "description": "Log merger that combines two log lists with keyed internal tables", + "testclasses_file": "reference/log_merger.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops", + "keyed_table_access" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1014/faulty/m0_frame_pricer.clas.abap b/tasks_gen/train/G1014/faulty/m0_frame_pricer.clas.abap new file mode 100644 index 0000000..5bda384 --- /dev/null +++ b/tasks_gen/train/G1014/faulty/m0_frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_true. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/faulty/m1_frame_pricer.clas.abap b/tasks_gen/train/G1014/faulty/m1_frame_pricer.clas.abap new file mode 100644 index 0000000..c723c71 --- /dev/null +++ b/tasks_gen/train/G1014/faulty/m1_frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm < 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/faulty/m2_frame_pricer.clas.abap b/tasks_gen/train/G1014/faulty/m2_frame_pricer.clas.abap new file mode 100644 index 0000000..fe01206 --- /dev/null +++ b/tasks_gen/train/G1014/faulty/m2_frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + OR iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/faulty/m3_frame_pricer.clas.abap b/tasks_gen/train/G1014/faulty/m3_frame_pricer.clas.abap new file mode 100644 index 0000000..8c530e7 --- /dev/null +++ b/tasks_gen/train/G1014/faulty/m3_frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass <> c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/faulty/m4_frame_pricer.clas.abap b/tasks_gen/train/G1014/faulty/m4_frame_pricer.clas.abap new file mode 100644 index 0000000..20f3f5c --- /dev/null +++ b/tasks_gen/train/G1014/faulty/m4_frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter / c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/generation.json b/tasks_gen/train/G1014/generation.json new file mode 100644 index 0000000..f53d616 --- /dev/null +++ b/tasks_gen/train/G1014/generation.json @@ -0,0 +1,52 @@ +{ + "id": "G1014", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/frame_pricer_hidden.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 28: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"20\"", + "hidden/frame_pricer_hidden.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 38: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 47: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 48: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 58: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"84\"", + "hidden/frame_pricer_hidden.clas.abap line 67: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 68: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 78: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"40\"", + "hidden/frame_pricer_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 98: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 107: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 108: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 117: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 118: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"" + ], + "by_check": { + "abaplint": 20 + } + }, + { + "stage": "validate", + "oracle": 79.2, + "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/G1014/hidden/frame_pricer_hidden.clas.abap b/tasks_gen/train/G1014/hidden/frame_pricer_hidden.clas.abap new file mode 100644 index 0000000..f67db01 --- /dev/null +++ b/tasks_gen/train/G1014/hidden/frame_pricer_hidden.clas.abap @@ -0,0 +1,171 @@ +CLASS {{p}}frame_pricer_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + CONSTANTS: + c_exp_standard TYPE ty_amount VALUE '59.20', + c_exp_premium TYPE ty_amount VALUE '155.00', + c_exp_gold TYPE ty_amount VALUE '271.00', + c_exp_small TYPE ty_amount VALUE '37.84', + c_exp_perimeter TYPE ty_amount VALUE '34.00', + c_exp_oversized TYPE ty_amount VALUE '119.40', + c_exp_area TYPE ty_amount VALUE '96.00', + c_exp_qty_five TYPE ty_amount VALUE '115.00', + c_exp_qty_four TYPE ty_amount VALUE '107.00', + c_exp_zero TYPE ty_amount VALUE '0.00'. + DATA mo_cut TYPE REF TO {{p}}frame_pricer. + METHODS setup. + METHODS standard_frame FOR TESTING. + METHODS premium_frame FOR TESTING. + METHODS gold_frame_no_glass FOR TESTING. + METHODS small_frame_surcharge FOR TESTING. + METHODS perimeter_at_limit FOR TESTING. + METHODS oversized_surcharge FOR TESTING. + METHODS area_at_limit FOR TESTING. + METHODS quantity_five_no_setup FOR TESTING. + METHODS quantity_four_setup FOR TESTING. + METHODS invalid_dimensions FOR TESTING. + METHODS invalid_moulding FOR TESTING. + METHODS invalid_glass FOR TESTING. +ENDCLASS. + + +CLASS {{p}}frame_pricer_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}frame_pricer( ). + ENDMETHOD. + + METHOD standard_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_standard + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD premium_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_premium + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'P' + iv_glass = 'A' + iv_quantity = 2 ) ). + ENDMETHOD. + + METHOD gold_frame_no_glass. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_gold + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'G' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD small_frame_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_small + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 14 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD perimeter_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_perimeter + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 15 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD oversized_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_oversized + act = mo_cut->calculate_price( iv_width_cm = 150 + iv_height_cm = 140 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD area_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_area + act = mo_cut->calculate_price( iv_width_cm = 100 + iv_height_cm = 200 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD quantity_five_no_setup. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_qty_five + act = mo_cut->calculate_price( iv_width_cm = 20 + iv_height_cm = 30 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD quantity_four_setup. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_qty_four + act = mo_cut->calculate_price( iv_width_cm = 20 + iv_height_cm = 30 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 4 ) ). + ENDMETHOD. + + METHOD invalid_dimensions. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 0 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) msg = `width` ). + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 0 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) msg = `height` ). + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 0 ) msg = `quantity` ). + ENDMETHOD. + + METHOD invalid_moulding. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'X' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD invalid_glass. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'X' + iv_quantity = 1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1014/mutation.json b/tasks_gen/train/G1014/mutation.json new file mode 100644 index 0000000..b118611 --- /dev/null +++ b/tasks_gen/train/G1014/mutation.json @@ -0,0 +1,79 @@ +{ + "task": "G1014", + "mutants": [ + { + "object": "{{P}}FRAME_PRICER", + "mutant": "line 89: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "0/12", + "failed_tests": [ + "AREA_AT_LIMIT", + "GOLD_FRAME_NO_GLASS", + "INVALID_DIMENSIONS", + "INVALID_GLASS", + "INVALID_MOULDING", + "OVERSIZED_SURCHARGE", + "PERIMETER_AT_LIMIT", + "PREMIUM_FRAME", + "QUANTITY_FIVE_NO_SETUP", + "QUANTITY_FOUR_SETUP", + "SMALL_FRAME_SURCHARGE", + "STANDARD_FRAME" + ] + }, + { + "object": "{{P}}FRAME_PRICER", + "mutant": "line 121: >= -> < (rel)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "AREA_AT_LIMIT", + "GOLD_FRAME_NO_GLASS", + "INVALID_DIMENSIONS", + "OVERSIZED_SURCHARGE", + "PERIMETER_AT_LIMIT", + "PREMIUM_FRAME", + "QUANTITY_FIVE_NO_SETUP", + "QUANTITY_FOUR_SETUP", + "SMALL_FRAME_SURCHARGE", + "STANDARD_FRAME" + ] + }, + { + "object": "{{P}}FRAME_PRICER", + "mutant": "line 122: AND -> OR (logic)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "INVALID_DIMENSIONS", + "INVALID_GLASS", + "INVALID_MOULDING" + ] + }, + { + "object": "{{P}}FRAME_PRICER", + "mutant": "line 127: = -> <> (eq)", + "status": "killed", + "hidden": "8/12", + "failed_tests": [ + "AREA_AT_LIMIT", + "INVALID_GLASS", + "OVERSIZED_SURCHARGE", + "STANDARD_FRAME" + ] + }, + { + "object": "{{P}}FRAME_PRICER", + "mutant": "line 158: * -> / (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "GOLD_FRAME_NO_GLASS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1014/reference/frame_pricer.clas.abap b/tasks_gen/train/G1014/reference/frame_pricer.clas.abap new file mode 100644 index 0000000..ffa43ca --- /dev/null +++ b/tasks_gen/train/G1014/reference/frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/reference/frame_pricer.testclasses.abap b/tasks_gen/train/G1014/reference/frame_pricer.testclasses.abap new file mode 100644 index 0000000..ff27bed --- /dev/null +++ b/tasks_gen/train/G1014/reference/frame_pricer.testclasses.abap @@ -0,0 +1,87 @@ +CLASS ltc_frame_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + CONSTANTS: + c_exp_standard TYPE ty_amount VALUE '59.20', + c_exp_premium TYPE ty_amount VALUE '155.00', + c_exp_small TYPE ty_amount VALUE '37.84', + c_exp_oversized TYPE ty_amount VALUE '119.40', + c_exp_gold TYPE ty_amount VALUE '271.00', + c_exp_zero TYPE ty_amount VALUE '0.00'. + DATA mo_cut TYPE REF TO {{p}}frame_pricer. + METHODS setup. + METHODS standard_order FOR TESTING. + METHODS premium_order FOR TESTING. + METHODS small_frame FOR TESTING. + METHODS oversized_frame FOR TESTING. + METHODS no_setup_fee_for_five FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS ltc_frame_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}frame_pricer( ). + ENDMETHOD. + + METHOD standard_order. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_standard + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD premium_order. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_premium + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'P' + iv_glass = 'A' + iv_quantity = 2 ) ). + ENDMETHOD. + + METHOD small_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_small + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 14 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD oversized_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_oversized + act = mo_cut->calculate_price( iv_width_cm = 150 + iv_height_cm = 140 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD no_setup_fee_for_five. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_gold + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'G' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD invalid_input. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 0 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1014/seed/frame_pricer.clas.abap b/tasks_gen/train/G1014/seed/frame_pricer.clas.abap new file mode 100644 index 0000000..0c4768b --- /dev/null +++ b/tasks_gen/train/G1014/seed/frame_pricer.clas.abap @@ -0,0 +1,127 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_standard TYPE ty_amount VALUE '0.08', + c_rate_premium TYPE ty_amount VALUE '0.15', + c_rate_gold TYPE ty_amount VALUE '0.28'. + + DATA gv_width TYPE ty_amount. + DATA gv_height TYPE ty_amount. + DATA gv_perimeter TYPE ty_amount. + DATA gv_area TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_frame TYPE ty_amount. + DATA lv_glass TYPE ty_amount. + DATA lv_extra TYPE ty_amount. + DATA lv_sum TYPE ty_amount. + DATA lv_qty TYPE ty_amount. + DATA lv_factor TYPE ty_amount. + DATA lv_base TYPE ty_amount. + DATA lv_labour TYPE ty_amount. + DATA lv_small TYPE ty_amount. + DATA lv_big TYPE ty_amount. + DATA lv_setup TYPE ty_amount. + DATA lv_limit_s TYPE ty_amount. + DATA lv_limit_b TYPE ty_amount. + + CLEAR rv_price. + +* check the input data + IF iv_width_cm LT 1 OR iv_height_cm LT 1. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_quantity LT 1. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_moulding NE 'S' AND iv_moulding NE 'P' AND iv_moulding NE 'G'. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_glass NE 'N' AND iv_glass NE 'S' AND iv_glass NE 'A'. + rv_price = 0. + RETURN. + ENDIF. + + MOVE iv_width_cm TO gv_width. + MOVE iv_height_cm TO gv_height. + MOVE 2 TO lv_factor. + COMPUTE gv_perimeter = ( gv_width + gv_height ) * lv_factor. + COMPUTE gv_area = gv_width * gv_height. + +* the price of the moulding + IF iv_moulding = 'S'. + COMPUTE lv_frame = gv_perimeter * c_rate_standard. + ELSE. + IF iv_moulding = 'P'. + COMPUTE lv_frame = gv_perimeter * c_rate_premium. + ELSE. + COMPUTE lv_frame = gv_perimeter * c_rate_gold. + ENDIF. + ENDIF. + +* the price of the glass + IF iv_glass = 'N'. + MOVE 0 TO lv_glass. + ELSE. + IF iv_glass = 'S'. + MOVE 18 TO lv_glass. + ELSE. + MOVE 34 TO lv_glass. + ENDIF. + ENDIF. + +* the extras + MOVE 3 TO lv_base. + MOVE 12 TO lv_labour. + MOVE 4 TO lv_small. + MOVE 25 TO lv_big. + MOVE 50 TO lv_limit_s. + MOVE 20000 TO lv_limit_b. + + COMPUTE lv_extra = lv_base + lv_labour. + + IF gv_perimeter LT lv_limit_s. + COMPUTE lv_extra = lv_extra + lv_small. + ENDIF. + + IF gv_area GT lv_limit_b. + COMPUTE lv_extra = lv_extra + lv_big. + ENDIF. + + COMPUTE lv_sum = lv_frame + lv_glass + lv_extra. + + MOVE iv_quantity TO lv_qty. + COMPUTE lv_sum = lv_sum * lv_qty. + + MOVE 15 TO lv_setup. + IF iv_quantity LT 5. + COMPUTE lv_sum = lv_sum + lv_setup. + ENDIF. + + rv_price = lv_sum. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1014/spec.md b/tasks_gen/train/G1014/spec.md new file mode 100644 index 0000000..e6948ce --- /dev/null +++ b/tasks_gen/train/G1014/spec.md @@ -0,0 +1,48 @@ +# 1. Goal +A picture framing shop prices a frame for a customer. The shop measures the +picture in centimetres and selects a moulding and a glass type. A checkout +program calls one method for each frame order and needs the total price. + +The class {{P}}FRAME_PRICER in package $TMP already does this. The code is hard +to read and hard to maintain. Refactor the class. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}FRAME_PRICER exists in package $TMP. It is active. +- The class is public, final, and has a public constructor without parameters. +- The class has the public types TY_MOULDING (CHAR 1), TY_GLASS (CHAR 1) and + TY_AMOUNT (packed number with 2 decimal places). +- The class has one public method CALCULATE_PRICE with the importing parameters + IV_WIDTH_CM, IV_HEIGHT_CM, IV_MOULDING, IV_GLASS and IV_QUANTITY and the + returning parameter RV_PRICE. +- The current implementation contains the complete calculation. Read it. + +# 4. Contract +- Refactor the class {{P}}FRAME_PRICER in package $TMP. +- Keep the class name, the class properties and the complete public interface. +- Do not add, remove or rename public methods, public types or parameters. +- The method CALCULATE_PRICE returns the same value as before for every input. + +# 5. Business rules +- The behavior of the class stays the same. Every input combination that the + current implementation accepts must produce the same result. +- The rules of the calculation are in the current implementation of the class. + Read them there. +- The hidden tests check the behavior with valid and invalid input values. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Each method has less than 40 statements. Use + local variables instead of attributes. Use constants instead of magic + numbers. Use modern ABAP statements and modern comparison operators. Give + each method one clear task. Do not change the public interface. +- Out of scope: do not create other objects. Do not change any other object in + package $TMP. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- The class has no ATC priority 1 or priority 2 finding. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1014/task.json b/tasks_gen/train/G1014/task.json new file mode 100644 index 0000000..a45f1b9 --- /dev/null +++ b/tasks_gen/train/G1014/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1014", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER", + "file": "seed/frame_pricer.clas.abap", + "description": "Legacy class that calculates the price of a picture frame" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER_HIDDEN", + "file": "hidden/frame_pricer_hidden.clas.abap", + "description": "Hidden tests for the unchanged behavior of the frame price calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER", + "file": "reference/frame_pricer.clas.abap", + "description": "Refactored frame price calculation", + "testclasses_file": "reference/frame_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1015/faulty/m0_towing_fee.clas.abap b/tasks_gen/train/G1015/faulty/m0_towing_fee.clas.abap new file mode 100644 index 0000000..a7e0de2 --- /dev/null +++ b/tasks_gen/train/G1015/faulty/m0_towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend <> abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/faulty/m1_towing_fee.clas.abap b/tasks_gen/train/G1015/faulty/m1_towing_fee.clas.abap new file mode 100644 index 0000000..0196426 --- /dev/null +++ b/tasks_gen/train/G1015/faulty/m1_towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class = 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/faulty/m2_towing_fee.clas.abap b/tasks_gen/train/G1015/faulty/m2_towing_fee.clas.abap new file mode 100644 index 0000000..b169174 --- /dev/null +++ b/tasks_gen/train/G1015/faulty/m2_towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 AND iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/faulty/m3_towing_fee.clas.abap b/tasks_gen/train/G1015/faulty/m3_towing_fee.clas.abap new file mode 100644 index 0000000..20914a0 --- /dev/null +++ b/tasks_gen/train/G1015/faulty/m3_towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 23 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/faulty/m4_towing_fee.clas.abap b/tasks_gen/train/G1015/faulty/m4_towing_fee.clas.abap new file mode 100644 index 0000000..c1f1752 --- /dev/null +++ b/tasks_gen/train/G1015/faulty/m4_towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step + 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/generation.json b/tasks_gen/train/G1015/generation.json new file mode 100644 index 0000000..6f19f35 --- /dev/null +++ b/tasks_gen/train/G1015/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1015", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "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/G1015/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1015/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..82be875 --- /dev/null +++ b/tasks_gen/train/G1015/hidden/t02_hidden.clas.abap @@ -0,0 +1,169 @@ +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}}towing_fee. + METHODS setup. + METHODS minimum_fee_applies FOR TESTING. + METHODS distance_charge FOR TESTING. + METHODS first_ten_km_free FOR TESTING. + METHODS night_surcharge FOR TESTING. + METHODS weekend_surcharge FOR TESTING. + METHODS night_and_weekend FOR TESTING. + METHODS night_hour_boundaries FOR TESTING. + METHODS rounds_up_to_five FOR TESTING. + METHODS large_vehicle_long_trip FOR TESTING. + METHODS rejects_unknown_class FOR TESTING. + METHODS rejects_bad_distance FOR TESTING. + METHODS rejects_bad_hour FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}towing_fee( ). + ENDMETHOD. + + METHOD minimum_fee_applies. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 5 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-ok + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 80 + act = ls_result-fee ). + ENDMETHOD. + + METHOD distance_charge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 130 + act = ls_result-fee ). + ENDMETHOD. + + METHOD first_ten_km_free. + DATA(ls_ten) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 12 + iv_is_weekend = abap_false ). + DATA(ls_twenty) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 20 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 90 + act = ls_ten-fee ). + cl_abap_unit_assert=>assert_equals( exp = 110 + act = ls_twenty-fee ). + ENDMETHOD. + + METHOD night_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 23 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 165 + act = ls_result-fee ). + ENDMETHOD. + + METHOD weekend_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 150 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_and_weekend. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 10 + iv_hour = 23 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 85 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_hour_boundaries. + DATA(ls_22) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 22 + iv_is_weekend = abap_false ). + DATA(ls_21) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 21 + iv_is_weekend = abap_false ). + DATA(ls_5) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 5 + iv_is_weekend = abap_false ). + DATA(ls_6) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 6 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 115 act = ls_22-fee msg = `hour 22` ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_21-fee msg = `hour 21` ). + cl_abap_unit_assert=>assert_equals( exp = 115 act = ls_5-fee msg = `hour 5` ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_6-fee msg = `hour 6` ). + ENDMETHOD. + + METHOD rounds_up_to_five. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 11 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 95 + act = ls_result-fee ). + ENDMETHOD. + + METHOD large_vehicle_long_trip. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'L' + iv_distance_km = 100 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 320 + act = ls_result-fee ). + ENDMETHOD. + + METHOD rejects_unknown_class. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'X' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_class + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + ENDMETHOD. + + METHOD rejects_bad_distance. + DATA(ls_zero) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 0 + iv_hour = 12 + iv_is_weekend = abap_false ). + DATA(ls_far) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 501 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_zero-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_far-status ). + ENDMETHOD. + + METHOD rejects_bad_hour. + DATA(ls_24) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 24 + iv_is_weekend = abap_false ). + DATA(ls_neg) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = -1 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_hour + act = ls_24-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_hour + act = ls_neg-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/mutation.json b/tasks_gen/train/G1015/mutation.json new file mode 100644 index 0000000..e35f46a --- /dev/null +++ b/tasks_gen/train/G1015/mutation.json @@ -0,0 +1,75 @@ +{ + "task": "G1015", + "mutants": [ + { + "object": "{{P}}TOWING_FEE", + "mutant": "line 80: = -> <> (eq)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "DISTANCE_CHARGE", + "FIRST_TEN_KM_FREE", + "LARGE_VEHICLE_LONG_TRIP", + "NIGHT_AND_WEEKEND", + "NIGHT_HOUR_BOUNDARIES", + "NIGHT_SURCHARGE", + "ROUNDS_UP_TO_FIVE", + "WEEKEND_SURCHARGE" + ] + }, + { + "object": "{{P}}TOWING_FEE", + "mutant": "line 92: <> -> = (rel)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "DISTANCE_CHARGE", + "FIRST_TEN_KM_FREE", + "NIGHT_HOUR_BOUNDARIES", + "NIGHT_SURCHARGE", + "REJECTS_BAD_DISTANCE", + "REJECTS_BAD_HOUR", + "REJECTS_UNKNOWN_CLASS", + "ROUNDS_UP_TO_FIVE", + "WEEKEND_SURCHARGE" + ] + }, + { + "object": "{{P}}TOWING_FEE", + "mutant": "line 96: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "REJECTS_BAD_HOUR" + ] + }, + { + "object": "{{P}}TOWING_FEE", + "mutant": "line 123: 22 -> 23 (const)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "NIGHT_HOUR_BOUNDARIES" + ] + }, + { + "object": "{{P}}TOWING_FEE", + "mutant": "line 131: - -> + (arith)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "DISTANCE_CHARGE", + "FIRST_TEN_KM_FREE", + "LARGE_VEHICLE_LONG_TRIP", + "MINIMUM_FEE_APPLIES", + "NIGHT_AND_WEEKEND", + "NIGHT_HOUR_BOUNDARIES", + "WEEKEND_SURCHARGE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1015/reference/towing_fee.clas.abap b/tasks_gen/train/G1015/reference/towing_fee.clas.abap new file mode 100644 index 0000000..a64f45f --- /dev/null +++ b/tasks_gen/train/G1015/reference/towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/reference/towing_fee.testclasses.abap b/tasks_gen/train/G1015/reference/towing_fee.testclasses.abap new file mode 100644 index 0000000..f05c923 --- /dev/null +++ b/tasks_gen/train/G1015/reference/towing_fee.testclasses.abap @@ -0,0 +1,74 @@ +CLASS ltc_towing_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}towing_fee. + METHODS setup. + METHODS minimum_fee FOR TESTING. + METHODS distance_charge FOR TESTING. + METHODS night_surcharge FOR TESTING. + METHODS weekend_surcharge FOR TESTING. + METHODS invalid_class FOR TESTING. + METHODS invalid_distance FOR TESTING. +ENDCLASS. + + +CLASS ltc_towing_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}towing_fee( ). + ENDMETHOD. + + METHOD minimum_fee. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 3 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-ok + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 80 + act = ls_result-fee ). + ENDMETHOD. + + METHOD distance_charge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 130 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 23 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 165 + act = ls_result-fee ). + ENDMETHOD. + + METHOD weekend_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 150 + act = ls_result-fee ). + ENDMETHOD. + + METHOD invalid_class. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'X' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_class + act = ls_result-status ). + ENDMETHOD. + + METHOD invalid_distance. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 0 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_result-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1015/spec.md b/tasks_gen/train/G1015/spec.md new file mode 100644 index 0000000..3d3848f --- /dev/null +++ b/tasks_gen/train/G1015/spec.md @@ -0,0 +1,67 @@ +# 1. Goal +A vehicle recovery service charges a call-out fee for a breakdown tow. A dispatch +program calls the calculation for each completed tow. The program needs the fee +and one status code for each call. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP is available. +- The class is self-contained. No other object is necessary. + +# 4. Contract +- Create the class {{P}}TOWING_FEE in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - TY_STATUS: character field, length 8. + - TY_AMOUNT: integer. + - TY_CLASS: character field, length 1. + - TY_RESULT: structure with the components STATUS (type TY_STATUS) and FEE + (type TY_AMOUNT). +- The class has the public constant structure C_STATUS with the components OK, + BAD_CLASS, BAD_DISTANCE and BAD_HOUR. The values are 'OK', 'CLASS', + 'DISTANCE' and 'HOUR'. +- The class has exactly one public method: + CALCULATE + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods. + +# 5. Business rules +The method CALCULATE applies the rules in this sequence. +1. Check the input. Return the status of the first check that fails: + 1.1 The vehicle class must be 'S', 'M' or 'L'. Else return BAD_CLASS. + 1.2 The distance must be from 1 to 500 kilometres. Else return BAD_DISTANCE. + 1.3 The hour must be from 0 to 23. Else return BAD_HOUR. + If a check fails, the fee is 0. +2. The base fee comes from the vehicle class: + S = 60 EUR, M = 90 EUR, L = 140 EUR. +3. The distance charge: the first 10 kilometres are free. Each kilometre above + 10 kilometres costs 2 EUR. +4. Add the base fee and the distance charge. The result is the subtotal. +5. The night is the hour 22, the hour 23 and the hours 0 to 5. If the call is at + night, calculate 25 percent of the subtotal. Round this night surcharge down + to a full euro. +6. If the call is on a weekend, calculate 15 percent of the subtotal. Round this + weekend surcharge down to a full euro. The night surcharge and the weekend + surcharge can apply together. Calculate both surcharges from the subtotal. +7. Add the subtotal and all surcharges. The result is the total. +8. If the total is below 80 EUR, the total is 80 EUR. +9. Round the total up to a full 5 EUR. If the total is already a full 5 EUR, the + total does not change. +10. Return the status OK and the fee. + +# 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 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/G1015/task.json b/tasks_gen/train/G1015/task.json new file mode 100644 index 0000000..a8c3d74 --- /dev/null +++ b/tasks_gen/train/G1015/task.json @@ -0,0 +1,41 @@ +{ + "id": "G1015", + "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}}TOWING_FEE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the towing fee calculator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOWING_FEE", + "file": "reference/towing_fee.clas.abap", + "description": "Towing call-out fee calculator", + "testclasses_file": "reference/towing_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1016/generation.json b/tasks_gen/train/G1016/generation.json new file mode 100644 index 0000000..3875e0c --- /dev/null +++ b/tasks_gen/train/G1016/generation.json @@ -0,0 +1,35 @@ +{ + "id": "G1016", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/return_checker.testclasses.abap: name damaged_without_code_is_rejected has 32 characters (ABAP max 30)", + "seed/vehicle.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/vehicle.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 3 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/train/G1016/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1016/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..a32c39d --- /dev/null +++ b/tasks_gen/train/G1016/hidden/t02_hidden.clas.abap @@ -0,0 +1,143 @@ +CLASS {{p}}t02_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}}return_checker. + DATA mo_env TYPE REF TO if_osql_test_environment. + + METHODS setup. + METHODS teardown. + METHODS valid_return + RETURNING VALUE(rs_return) TYPE {{p}}return_checker=>ty_return_data. + METHODS expect_reason + IMPORTING is_data TYPE {{p}}return_checker=>ty_return_data + iv_reason TYPE ty_reason. + + METHODS ok_return FOR TESTING. + METHODS rejects_no_data FOR TESTING. + METHODS rejects_empty_date FOR TESTING. + METHODS rejects_date_order FOR TESTING. + METHODS rejects_mileage FOR TESTING. + METHODS rejects_fuel FOR TESTING. + METHODS rejects_damage FOR TESTING. + METHODS rejects_unknown FOR TESTING. + METHODS rejects_too_long FOR TESTING. + METHODS accepts_max_duration FOR TESTING. + METHODS checks_data_before_master FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_env = cl_osql_test_environment=>create( VALUE #( ( '{{P}}VEHICLE' ) ) ). + DATA lt_vehicle TYPE STANDARD TABLE OF {{p}}vehicle WITH EMPTY KEY. + lt_vehicle = VALUE #( ( mandt = sy-mandt + vehicle_id = 'V-1001' + max_days = 5 ) + ( mandt = sy-mandt + vehicle_id = 'V-1002' + max_days = 30 ) ). + mo_env->insert_test_data( lt_vehicle ). + mo_cut = NEW {{p}}return_checker( ). + ENDMETHOD. + + METHOD teardown. + mo_env->destroy( ). + ENDMETHOD. + + METHOD valid_return. + rs_return = VALUE #( rental_id = 'R-4711' + vehicle_id = 'V-1001' + start_date = '20240101' + return_date = '20240103' + start_mileage = 10000 + end_mileage = 10250 + fuel_percent = 80 + damage_code = '' + is_damaged = abap_false ). + ENDMETHOD. + + METHOD expect_reason. + TRY. + mo_cut->check( is_data ). + cl_abap_unit_assert=>fail( msg = |no exception, expected { iv_reason }| ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD ok_return. + TRY. + mo_cut->check( valid_return( ) ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lo_error->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_no_data. + DATA(ls_return) = valid_return( ). + CLEAR ls_return-rental_id. + expect_reason( is_data = ls_return iv_reason = 'NO_DATA' ). + ENDMETHOD. + + METHOD rejects_empty_date. + DATA(ls_return) = valid_return( ). + CLEAR ls_return-start_date. + expect_reason( is_data = ls_return iv_reason = 'NO_DATA' ). + ENDMETHOD. + + METHOD rejects_date_order. + DATA(ls_return) = valid_return( ). + ls_return-return_date = '20231231'. + expect_reason( is_data = ls_return iv_reason = 'DATE_ORDER' ). + ENDMETHOD. + + METHOD rejects_mileage. + DATA(ls_return) = valid_return( ). + ls_return-end_mileage = ls_return-start_mileage - 1. + expect_reason( is_data = ls_return iv_reason = 'MILEAGE' ). + ENDMETHOD. + + METHOD rejects_fuel. + DATA(ls_return) = valid_return( ). + ls_return-fuel_percent = 101. + expect_reason( is_data = ls_return iv_reason = 'FUEL' ). + ENDMETHOD. + + METHOD rejects_damage. + DATA(ls_return) = valid_return( ). + ls_return-is_damaged = abap_true. + CLEAR ls_return-damage_code. + expect_reason( is_data = ls_return iv_reason = 'DAMAGE' ). + ENDMETHOD. + + METHOD rejects_unknown. + DATA(ls_return) = valid_return( ). + ls_return-vehicle_id = 'V-9999'. + expect_reason( is_data = ls_return iv_reason = 'UNKNOWN' ). + ENDMETHOD. + + METHOD rejects_too_long. + DATA(ls_return) = valid_return( ). + ls_return-return_date = '20240110'. + expect_reason( is_data = ls_return iv_reason = 'TOO_LONG' ). + ENDMETHOD. + + METHOD accepts_max_duration. + DATA(ls_return) = valid_return( ). + ls_return-return_date = '20240105'. + TRY. + mo_cut->check( ls_return ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lo_error->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD checks_data_before_master. + DATA(ls_return) = valid_return( ). + ls_return-vehicle_id = 'V-9999'. + ls_return-fuel_percent = 150. + expect_reason( is_data = ls_return iv_reason = 'FUEL' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1016/reference/cx_return_error.clas.abap b/tasks_gen/train/G1016/reference/cx_return_error.clas.abap new file mode 100644 index 0000000..507ed9b --- /dev/null +++ b/tasks_gen/train/G1016/reference/cx_return_error.clas.abap @@ -0,0 +1,28 @@ +CLASS {{p}}cx_return_error DEFINITION PUBLIC INHERITING FROM cx_static_check FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS: + BEGIN OF c_reason, + no_data TYPE ty_reason VALUE 'NO_DATA', + date_order TYPE ty_reason VALUE 'DATE_ORDER', + mileage TYPE ty_reason VALUE 'MILEAGE', + fuel TYPE ty_reason VALUE 'FUEL', + damage TYPE ty_reason VALUE 'DAMAGE', + unknown TYPE ty_reason VALUE 'UNKNOWN', + too_long TYPE ty_reason VALUE 'TOO_LONG', + END OF c_reason. + + DATA mv_reason TYPE ty_reason READ-ONLY. + + METHODS constructor + IMPORTING iv_reason TYPE ty_reason. +ENDCLASS. + + +CLASS {{p}}cx_return_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1016/reference/return_checker.clas.abap b/tasks_gen/train/G1016/reference/return_checker.clas.abap new file mode 100644 index 0000000..06ba7df --- /dev/null +++ b/tasks_gen/train/G1016/reference/return_checker.clas.abap @@ -0,0 +1,86 @@ +CLASS {{p}}return_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_return_data, + rental_id TYPE string, + vehicle_id TYPE string, + start_date TYPE d, + return_date TYPE d, + start_mileage TYPE i, + end_mileage TYPE i, + fuel_percent TYPE i, + damage_code TYPE string, + is_damaged TYPE abap_bool, + END OF ty_return_data. + + METHODS constructor. + METHODS check + IMPORTING is_return_data TYPE ty_return_data + RAISING {{p}}cx_return_error. + + PRIVATE SECTION. + METHODS has_missing_data + IMPORTING is_return_data TYPE ty_return_data + RETURNING VALUE(rv_missing) TYPE abap_bool. + METHODS duration_in_days + IMPORTING is_return_data TYPE ty_return_data + RETURNING VALUE(rv_days) TYPE i. +ENDCLASS. + + +CLASS {{p}}return_checker IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD check. + IF has_missing_data( is_return_data ) = abap_true. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-no_data. + ENDIF. + + IF is_return_data-return_date < is_return_data-start_date. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-date_order. + ENDIF. + + IF is_return_data-end_mileage < is_return_data-start_mileage. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-mileage. + ENDIF. + + IF is_return_data-fuel_percent < 0 OR is_return_data-fuel_percent > 100. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-fuel. + ENDIF. + + IF is_return_data-is_damaged = abap_true AND is_return_data-damage_code IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-damage. + ENDIF. + + DATA lv_max_days TYPE i. + SELECT SINGLE max_days FROM {{p}}vehicle + WHERE vehicle_id = @is_return_data-vehicle_id + INTO @lv_max_days. + IF sy-subrc <> 0. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-unknown. + ENDIF. + + IF duration_in_days( is_return_data ) > lv_max_days. + RAISE EXCEPTION TYPE {{p}}cx_return_error + EXPORTING iv_reason = {{p}}cx_return_error=>c_reason-too_long. + ENDIF. + ENDMETHOD. + + METHOD has_missing_data. + rv_missing = xsdbool( is_return_data-rental_id IS INITIAL + OR is_return_data-vehicle_id IS INITIAL + OR is_return_data-start_date IS INITIAL + OR is_return_data-return_date IS INITIAL ). + ENDMETHOD. + + METHOD duration_in_days. + rv_days = is_return_data-return_date - is_return_data-start_date + 1. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1016/reference/return_checker.testclasses.abap b/tasks_gen/train/G1016/reference/return_checker.testclasses.abap new file mode 100644 index 0000000..14010d3 --- /dev/null +++ b/tasks_gen/train/G1016/reference/return_checker.testclasses.abap @@ -0,0 +1,99 @@ +CLASS ltc_return_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}return_checker. + DATA mo_env TYPE REF TO if_osql_test_environment. + + METHODS setup. + METHODS teardown. + METHODS test_return + RETURNING VALUE(rs_return) TYPE {{p}}return_checker=>ty_return_data. + + METHODS valid_return_is_accepted FOR TESTING. + METHODS missing_rental_id_is_rejected FOR TESTING. + METHODS unknown_vehicle_is_rejected FOR TESTING. + METHODS too_long_rental_is_rejected FOR TESTING. + METHODS damage_without_code_fails FOR TESTING. +ENDCLASS. + + +CLASS ltc_return_checker IMPLEMENTATION. + METHOD setup. + mo_env = cl_osql_test_environment=>create( VALUE #( ( '{{P}}VEHICLE' ) ) ). + DATA lt_vehicle TYPE STANDARD TABLE OF {{p}}vehicle WITH EMPTY KEY. + lt_vehicle = VALUE #( ( mandt = sy-mandt + vehicle_id = 'V-1001' + max_days = 5 ) ). + mo_env->insert_test_data( lt_vehicle ). + mo_cut = NEW {{p}}return_checker( ). + ENDMETHOD. + + METHOD teardown. + mo_env->destroy( ). + ENDMETHOD. + + METHOD test_return. + rs_return = VALUE #( rental_id = 'R-4711' + vehicle_id = 'V-1001' + start_date = '20240101' + return_date = '20240103' + start_mileage = 10000 + end_mileage = 10250 + fuel_percent = 80 + damage_code = '' + is_damaged = abap_false ). + ENDMETHOD. + + METHOD valid_return_is_accepted. + TRY. + mo_cut->check( test_return( ) ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( msg = |unexpected reason { lo_error->mv_reason }| ). + ENDTRY. + ENDMETHOD. + + METHOD missing_rental_id_is_rejected. + DATA(ls_return) = test_return( ). + CLEAR ls_return-rental_id. + TRY. + mo_cut->check( ls_return ). + cl_abap_unit_assert=>fail( msg = 'NO_DATA expected' ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'NO_DATA' act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD unknown_vehicle_is_rejected. + DATA(ls_return) = test_return( ). + ls_return-vehicle_id = 'V-9999'. + TRY. + mo_cut->check( ls_return ). + cl_abap_unit_assert=>fail( msg = 'UNKNOWN expected' ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD too_long_rental_is_rejected. + DATA(ls_return) = test_return( ). + ls_return-return_date = '20240120'. + TRY. + mo_cut->check( ls_return ). + cl_abap_unit_assert=>fail( msg = 'TOO_LONG expected' ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'TOO_LONG' act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD damage_without_code_fails. + DATA(ls_return) = test_return( ). + ls_return-is_damaged = abap_true. + CLEAR ls_return-damage_code. + TRY. + mo_cut->check( ls_return ). + cl_abap_unit_assert=>fail( msg = 'DAMAGE expected' ). + CATCH {{p}}cx_return_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 'DAMAGE' act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1016/seed/seed_vehicle.clas.abap b/tasks_gen/train/G1016/seed/seed_vehicle.clas.abap new file mode 100644 index 0000000..13b768d --- /dev/null +++ b/tasks_gen/train/G1016/seed/seed_vehicle.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}seed_vehicle DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_vehicle IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}vehicle. + INSERT {{p}}vehicle FROM TABLE @( VALUE #( + ( mandt = sy-mandt vehicle_id = 'V-1001' max_days = 5 ) + ( mandt = sy-mandt vehicle_id = 'V-1002' max_days = 30 ) + ( mandt = sy-mandt vehicle_id = 'V-1003' max_days = 3 ) ) ). + out->write( |Rows: { sy-dbcnt }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1016/seed/vehicle.tabl.abap b/tasks_gen/train/G1016/seed/vehicle.tabl.abap new file mode 100644 index 0000000..5373153 --- /dev/null +++ b/tasks_gen/train/G1016/seed/vehicle.tabl.abap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Vehicle master data' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}vehicle { + key mandt : abap.clnt not null; + key vehicle_id : abap.char(10) not null; + max_days : abap.int4; +} diff --git a/tasks_gen/train/G1016/spec.md b/tasks_gen/train/G1016/spec.md new file mode 100644 index 0000000..7b35bae --- /dev/null +++ b/tasks_gen/train/G1016/spec.md @@ -0,0 +1,72 @@ +# 1. Goal +A car rental company checks the return of a rental car. The return desk calls +the check before it closes the rental contract. The check reports the first +problem that it finds. + +# 2. Open questions +None. + +# 3. Context +- The table {{P}}VEHICLE is in package $TMP. It is active. The table contains + the master data of the rental vehicles: + - MANDT: client, key. + - VEHICLE_ID: CHAR 10, key. The identifier of the vehicle. + - MAX_DAYS: INT4. The maximum number of rental days for this vehicle. +- The seed class {{P}}SEED_VEHICLE fills the table with example data. + +# 4. Contract +- Create the exception class {{P}}CX_RETURN_ERROR in package $TMP. + - The class inherits from CX_STATIC_CHECK. + - The class has the public attribute MV_REASON. MV_REASON has a character + type with length 10. + - MV_REASON has one of these values: NO_DATA, DATE_ORDER, MILEAGE, FUEL, + DAMAGE, UNKNOWN, TOO_LONG. + - Do not add other public methods. +- Create the class {{P}}RETURN_CHECKER in package $TMP. + - The class is public and final. It has a public constructor without + parameters. + - The class has this public type: + TYPES: + BEGIN OF ty_return_data, + rental_id TYPE string, + vehicle_id TYPE string, + start_date TYPE d, + return_date TYPE d, + start_mileage TYPE i, + end_mileage TYPE i, + fuel_percent TYPE i, + damage_code TYPE string, + is_damaged TYPE abap_bool, + END OF ty_return_data. + - The class has this public method: + METHODS check + IMPORTING is_return_data TYPE ty_return_data + RAISING {{P}}CX_RETURN_ERROR. + - Do not add other public methods. + +# 5. Business rules +Check the return data. Apply the rules in this sequence. The check reports the +reason of the first rule that fails. If no rule fails, the check does not raise +an exception. +1. NO_DATA: RENTAL_ID or VEHICLE_ID is empty, or START_DATE or RETURN_DATE is + initial. +2. DATE_ORDER: RETURN_DATE is before START_DATE. +3. MILEAGE: END_MILEAGE is smaller than START_MILEAGE. +4. FUEL: FUEL_PERCENT is smaller than 0 or larger than 100. +5. DAMAGE: IS_DAMAGED is 'X' and DAMAGE_CODE is empty. +6. UNKNOWN: VEHICLE_ID is not in the table {{P}}VEHICLE. +7. TOO_LONG: the rental duration is larger than MAX_DAYS of the vehicle. +The rental duration is the number of calendar days from START_DATE to +RETURN_DATE. Both days are included. Example: start date 2024-01-01 and return +date 2024-01-03 give a duration of 3 days. + +# 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 {{P}}VEHICLE and {{P}}SEED_VEHICLE. + +# 7. Acceptance +- The classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}RETURN_CHECKER. diff --git a/tasks_gen/train/G1016/task.json b/tasks_gen/train/G1016/task.json new file mode 100644 index 0000000..a80fd48 --- /dev/null +++ b/tasks_gen/train/G1016/task.json @@ -0,0 +1,68 @@ +{ + "id": "G1016", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}VEHICLE", + "file": "seed/vehicle.tabl.abap", + "description": "Vehicle master data with the maximum rental days" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_VEHICLE", + "file": "seed/seed_vehicle.clas.abap", + "description": "Fills {{P}}VEHICLE with example data", + "run": true + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_RETURN_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}RETURN_CHECKER" + } + ], + "out_of_scope": [ + "{{P}}VEHICLE", + "{{P}}SEED_VEHICLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "Hidden tests for the rental return check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_RETURN_ERROR", + "file": "reference/cx_return_error.clas.abap", + "description": "Exception for a rejected rental return" + }, + { + "type": "CLAS", + "name": "{{P}}RETURN_CHECKER", + "file": "reference/return_checker.clas.abap", + "description": "Checks the return of a rental car", + "testclasses_file": "reference/return_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1017/faulty/m0_inspection_checker.clas.abap b/tasks_gen/train/G1017/faulty/m0_inspection_checker.clas.abap new file mode 100644 index 0000000..2b7e020 --- /dev/null +++ b/tasks_gen/train/G1017/faulty/m0_inspection_checker.clas.abap @@ -0,0 +1,67 @@ +CLASS {{p}}inspection_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_inspection_check. + + PRIVATE SECTION. + METHODS normalize + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. + + METHODS age_in_years + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rv_years) TYPE i. +ENDCLASS. + + +CLASS {{p}}inspection_checker IMPLEMENTATION. + METHOD {{p}}if_inspection_check~check_vehicle. + IF normalize( iv_plate ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_plate. + RETURN. + ENDIF. + + IF normalize( iv_make ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_make. + RETURN. + ENDIF. + + IF iv_first_reg IS INITIAL AND iv_first_reg > iv_check_date. + rv_reason = {{p}}if_inspection_check=>c_reason-reg_date. + RETURN. + ENDIF. + + IF age_in_years( iv_from = iv_first_reg iv_to = iv_check_date ) >= 30. + rv_reason = {{p}}if_inspection_check=>c_reason-too_old. + RETURN. + ENDIF. + + IF iv_mileage_km < 0. + rv_reason = {{p}}if_inspection_check=>c_reason-mileage. + RETURN. + ENDIF. + + IF iv_brake_pct < 50. + rv_reason = {{p}}if_inspection_check=>c_reason-brakes. + RETURN. + ENDIF. + + IF iv_exhaust_ppm > 1000. + rv_reason = {{p}}if_inspection_check=>c_reason-exhaust. + RETURN. + ENDIF. + + rv_reason = {{p}}if_inspection_check=>c_reason-ok. + ENDMETHOD. + + METHOD normalize. + rv_text = to_upper( replace( val = iv_text sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD age_in_years. + rv_years = CONV i( iv_to(4) ) - CONV i( iv_from(4) ). + IF iv_to+4(4) < iv_from+4(4). + rv_years = rv_years - 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/faulty/m2_inspection_checker.clas.abap b/tasks_gen/train/G1017/faulty/m2_inspection_checker.clas.abap new file mode 100644 index 0000000..16ce3dc --- /dev/null +++ b/tasks_gen/train/G1017/faulty/m2_inspection_checker.clas.abap @@ -0,0 +1,67 @@ +CLASS {{p}}inspection_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_inspection_check. + + PRIVATE SECTION. + METHODS normalize + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. + + METHODS age_in_years + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rv_years) TYPE i. +ENDCLASS. + + +CLASS {{p}}inspection_checker IMPLEMENTATION. + METHOD {{p}}if_inspection_check~check_vehicle. + IF normalize( iv_plate ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_plate. + RETURN. + ENDIF. + + IF normalize( iv_make ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_make. + RETURN. + ENDIF. + + IF iv_first_reg IS INITIAL OR iv_first_reg > iv_check_date. + rv_reason = {{p}}if_inspection_check=>c_reason-reg_date. + RETURN. + ENDIF. + + IF age_in_years( iv_from = iv_first_reg iv_to = iv_check_date ) >= 30. + rv_reason = {{p}}if_inspection_check=>c_reason-too_old. + RETURN. + ENDIF. + + IF iv_mileage_km < 0. + rv_reason = {{p}}if_inspection_check=>c_reason-mileage. + RETURN. + ENDIF. + + IF iv_brake_pct < 50. + rv_reason = {{p}}if_inspection_check=>c_reason-brakes. + RETURN. + ENDIF. + + IF iv_exhaust_ppm <= 1000. + rv_reason = {{p}}if_inspection_check=>c_reason-exhaust. + RETURN. + ENDIF. + + rv_reason = {{p}}if_inspection_check=>c_reason-ok. + ENDMETHOD. + + METHOD normalize. + rv_text = to_upper( replace( val = iv_text sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD age_in_years. + rv_years = CONV i( iv_to(4) ) - CONV i( iv_from(4) ). + IF iv_to+4(4) < iv_from+4(4). + rv_years = rv_years - 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/faulty/m3_inspection_checker.clas.abap b/tasks_gen/train/G1017/faulty/m3_inspection_checker.clas.abap new file mode 100644 index 0000000..be325e2 --- /dev/null +++ b/tasks_gen/train/G1017/faulty/m3_inspection_checker.clas.abap @@ -0,0 +1,67 @@ +CLASS {{p}}inspection_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_inspection_check. + + PRIVATE SECTION. + METHODS normalize + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. + + METHODS age_in_years + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rv_years) TYPE i. +ENDCLASS. + + +CLASS {{p}}inspection_checker IMPLEMENTATION. + METHOD {{p}}if_inspection_check~check_vehicle. + IF normalize( iv_plate ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_plate. + RETURN. + ENDIF. + + IF normalize( iv_make ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_make. + RETURN. + ENDIF. + + IF iv_first_reg IS INITIAL OR iv_first_reg > iv_check_date. + rv_reason = {{p}}if_inspection_check=>c_reason-reg_date. + RETURN. + ENDIF. + + IF age_in_years( iv_from = iv_first_reg iv_to = iv_check_date ) >= 30. + rv_reason = {{p}}if_inspection_check=>c_reason-too_old. + RETURN. + ENDIF. + + IF iv_mileage_km < 0. + rv_reason = {{p}}if_inspection_check=>c_reason-mileage. + RETURN. + ENDIF. + + IF iv_brake_pct < 50. + rv_reason = {{p}}if_inspection_check=>c_reason-brakes. + RETURN. + ENDIF. + + IF iv_exhaust_ppm > 1000. + rv_reason = {{p}}if_inspection_check=>c_reason-exhaust. + RETURN. + ENDIF. + + rv_reason = {{p}}if_inspection_check=>c_reason-ok. + ENDMETHOD. + + METHOD normalize. + rv_text = to_upper( replace( val = iv_text sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD age_in_years. + rv_years = CONV i( iv_to(4) ) + CONV i( iv_from(4) ). + IF iv_to+4(4) < iv_from+4(4). + rv_years = rv_years - 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/generation.json b/tasks_gen/train/G1017/generation.json new file mode 100644 index 0000000..654b3e8 --- /dev/null +++ b/tasks_gen/train/G1017/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1017", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "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/G1017/hidden/t_f01_hidden.clas.abap b/tasks_gen/train/G1017/hidden/t_f01_hidden.clas.abap new file mode 100644 index 0000000..9e4303f --- /dev/null +++ b/tasks_gen/train/G1017/hidden/t_f01_hidden.clas.abap @@ -0,0 +1,119 @@ +CLASS {{p}}t_f01_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_inspection_check. + + METHODS setup. + METHODS reason_of + IMPORTING iv_plate TYPE string DEFAULT `B-AB 1234` + iv_make TYPE string DEFAULT `VW` + iv_first_reg TYPE d DEFAULT '20150101' + iv_check_date TYPE d DEFAULT '20250615' + iv_mileage_km TYPE i DEFAULT 100000 + iv_brake_pct TYPE i DEFAULT 60 + iv_exhaust_ppm TYPE i DEFAULT 1000 + RETURNING VALUE(rv_reason) TYPE {{p}}if_inspection_check=>ty_reason. + + METHODS ok_vehicle FOR TESTING. + METHODS plate_with_spaces FOR TESTING. + METHODS empty_plate FOR TESTING. + METHODS empty_make FOR TESTING. + METHODS future_reg_date FOR TESTING. + METHODS initial_reg_date FOR TESTING. + METHODS age_29_years FOR TESTING. + METHODS age_30_years FOR TESTING. + METHODS negative_mileage FOR TESTING. + METHODS weak_brakes FOR TESTING. + METHODS brake_boundary FOR TESTING. + METHODS high_exhaust FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_f01_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}inspection_checker( ). + ENDMETHOD. + + METHOD reason_of. + rv_reason = mo_cut->check_vehicle( + iv_plate = iv_plate + iv_make = iv_make + iv_first_reg = iv_first_reg + iv_check_date = iv_check_date + iv_mileage_km = iv_mileage_km + iv_brake_pct = iv_brake_pct + iv_exhaust_ppm = iv_exhaust_ppm ). + ENDMETHOD. + + METHOD ok_vehicle. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-ok + act = reason_of( ) ). + ENDMETHOD. + + METHOD plate_with_spaces. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-ok + act = reason_of( iv_plate = ` b-ab 1234 ` ) ). + ENDMETHOD. + + METHOD empty_plate. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-no_plate + act = reason_of( iv_plate = ` ` ) ). + ENDMETHOD. + + METHOD empty_make. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-no_make + act = reason_of( iv_make = `` ) ). + ENDMETHOD. + + METHOD future_reg_date. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-reg_date + act = reason_of( iv_first_reg = '20260101' ) ). + ENDMETHOD. + + METHOD initial_reg_date. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-reg_date + act = reason_of( iv_first_reg = '00000000' ) ). + ENDMETHOD. + + METHOD age_29_years. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-ok + act = reason_of( iv_first_reg = '19950615' iv_check_date = '20250614' ) ). + ENDMETHOD. + + METHOD age_30_years. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-too_old + act = reason_of( iv_first_reg = '19950615' iv_check_date = '20250615' ) ). + ENDMETHOD. + + METHOD negative_mileage. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-mileage + act = reason_of( iv_mileage_km = -1 ) ). + ENDMETHOD. + + METHOD weak_brakes. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-brakes + act = reason_of( iv_brake_pct = 49 ) ). + ENDMETHOD. + + METHOD brake_boundary. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-ok + act = reason_of( iv_brake_pct = 50 ) ). + ENDMETHOD. + + METHOD high_exhaust. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-exhaust + act = reason_of( iv_exhaust_ppm = 1001 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/mutation.json b/tasks_gen/train/G1017/mutation.json new file mode 100644 index 0000000..663a3af --- /dev/null +++ b/tasks_gen/train/G1017/mutation.json @@ -0,0 +1,61 @@ +{ + "task": "G1017", + "mutants": [ + { + "object": "{{P}}INSPECTION_CHECKER", + "mutant": "line 29: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "FUTURE_REG_DATE", + "INITIAL_REG_DATE" + ] + }, + { + "object": "{{P}}INSPECTION_CHECKER", + "mutant": "line 34: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}INSPECTION_CHECKER", + "mutant": "line 49: > -> <= (rel)", + "status": "killed", + "hidden": "7/12", + "failed_tests": [ + "AGE_29_YEARS", + "BRAKE_BOUNDARY", + "HIGH_EXHAUST", + "OK_VEHICLE", + "PLATE_WITH_SPACES" + ] + }, + { + "object": "{{P}}INSPECTION_CHECKER", + "mutant": "line 62: - -> + (arith)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "AGE_29_YEARS", + "BRAKE_BOUNDARY", + "HIGH_EXHAUST", + "NEGATIVE_MILEAGE", + "OK_VEHICLE", + "PLATE_WITH_SPACES", + "WEAK_BRAKES" + ] + }, + { + "object": "{{P}}INSPECTION_CHECKER", + "mutant": "line 63: 4 -> 5 (const)", + "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/G1017/reference/inspection_checker.clas.abap b/tasks_gen/train/G1017/reference/inspection_checker.clas.abap new file mode 100644 index 0000000..8425a2d --- /dev/null +++ b/tasks_gen/train/G1017/reference/inspection_checker.clas.abap @@ -0,0 +1,67 @@ +CLASS {{p}}inspection_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_inspection_check. + + PRIVATE SECTION. + METHODS normalize + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. + + METHODS age_in_years + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rv_years) TYPE i. +ENDCLASS. + + +CLASS {{p}}inspection_checker IMPLEMENTATION. + METHOD {{p}}if_inspection_check~check_vehicle. + IF normalize( iv_plate ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_plate. + RETURN. + ENDIF. + + IF normalize( iv_make ) IS INITIAL. + rv_reason = {{p}}if_inspection_check=>c_reason-no_make. + RETURN. + ENDIF. + + IF iv_first_reg IS INITIAL OR iv_first_reg > iv_check_date. + rv_reason = {{p}}if_inspection_check=>c_reason-reg_date. + RETURN. + ENDIF. + + IF age_in_years( iv_from = iv_first_reg iv_to = iv_check_date ) >= 30. + rv_reason = {{p}}if_inspection_check=>c_reason-too_old. + RETURN. + ENDIF. + + IF iv_mileage_km < 0. + rv_reason = {{p}}if_inspection_check=>c_reason-mileage. + RETURN. + ENDIF. + + IF iv_brake_pct < 50. + rv_reason = {{p}}if_inspection_check=>c_reason-brakes. + RETURN. + ENDIF. + + IF iv_exhaust_ppm > 1000. + rv_reason = {{p}}if_inspection_check=>c_reason-exhaust. + RETURN. + ENDIF. + + rv_reason = {{p}}if_inspection_check=>c_reason-ok. + ENDMETHOD. + + METHOD normalize. + rv_text = to_upper( replace( val = iv_text sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD age_in_years. + rv_years = CONV i( iv_to(4) ) - CONV i( iv_from(4) ). + IF iv_to+4(4) < iv_from+4(4). + rv_years = rv_years - 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/reference/inspection_checker.testclasses.abap b/tasks_gen/train/G1017/reference/inspection_checker.testclasses.abap new file mode 100644 index 0000000..5edac7c --- /dev/null +++ b/tasks_gen/train/G1017/reference/inspection_checker.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_inspection_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_inspection_check. + + METHODS setup. + METHODS reason_of + IMPORTING iv_plate TYPE string DEFAULT `B-AB 1234` + iv_make TYPE string DEFAULT `VW` + iv_first_reg TYPE d DEFAULT '20150101' + iv_check_date TYPE d DEFAULT '20250615' + iv_mileage_km TYPE i DEFAULT 100000 + iv_brake_pct TYPE i DEFAULT 60 + iv_exhaust_ppm TYPE i DEFAULT 500 + RETURNING VALUE(rv_reason) TYPE {{p}}if_inspection_check=>ty_reason. + + METHODS ok_vehicle FOR TESTING. + METHODS empty_plate FOR TESTING. + METHODS empty_make FOR TESTING. + METHODS future_reg_date FOR TESTING. + METHODS old_vehicle FOR TESTING. + METHODS weak_brakes FOR TESTING. + METHODS high_exhaust FOR TESTING. +ENDCLASS. + + +CLASS ltc_inspection_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}inspection_checker( ). + ENDMETHOD. + + METHOD reason_of. + rv_reason = mo_cut->check_vehicle( + iv_plate = iv_plate + iv_make = iv_make + iv_first_reg = iv_first_reg + iv_check_date = iv_check_date + iv_mileage_km = iv_mileage_km + iv_brake_pct = iv_brake_pct + iv_exhaust_ppm = iv_exhaust_ppm ). + ENDMETHOD. + + METHOD ok_vehicle. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-ok + act = reason_of( ) ). + ENDMETHOD. + + METHOD empty_plate. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-no_plate + act = reason_of( iv_plate = ` ` ) ). + ENDMETHOD. + + METHOD empty_make. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-no_make + act = reason_of( iv_make = ` ` ) ). + ENDMETHOD. + + METHOD future_reg_date. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-reg_date + act = reason_of( iv_first_reg = '20300101' ) ). + ENDMETHOD. + + METHOD old_vehicle. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-too_old + act = reason_of( iv_first_reg = '19800101' ) ). + ENDMETHOD. + + METHOD weak_brakes. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-brakes + act = reason_of( iv_brake_pct = 20 ) ). + ENDMETHOD. + + METHOD high_exhaust. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_inspection_check=>c_reason-exhaust + act = reason_of( iv_exhaust_ppm = 5000 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1017/seed/if_inspection_check.intf.abap b/tasks_gen/train/G1017/seed/if_inspection_check.intf.abap new file mode 100644 index 0000000..cbcdd66 --- /dev/null +++ b/tasks_gen/train/G1017/seed/if_inspection_check.intf.abap @@ -0,0 +1,25 @@ +INTERFACE {{p}}if_inspection_check PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + no_plate TYPE ty_reason VALUE 'NO_PLATE', + no_make TYPE ty_reason VALUE 'NO_MAKE', + reg_date TYPE ty_reason VALUE 'REG_DATE', + too_old TYPE ty_reason VALUE 'TOO_OLD', + mileage TYPE ty_reason VALUE 'MILEAGE', + brakes TYPE ty_reason VALUE 'BRAKES', + exhaust TYPE ty_reason VALUE 'EXHAUST', + END OF c_reason. + + METHODS check_vehicle + IMPORTING iv_plate TYPE string + iv_make TYPE string + iv_first_reg TYPE d + iv_check_date TYPE d + iv_mileage_km TYPE i + iv_brake_pct TYPE i + iv_exhaust_ppm TYPE i + RETURNING VALUE(rv_reason) TYPE ty_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1017/spec.md b/tasks_gen/train/G1017/spec.md new file mode 100644 index 0000000..e7011d7 --- /dev/null +++ b/tasks_gen/train/G1017/spec.md @@ -0,0 +1,50 @@ +# 1. Goal +A vehicle inspection station checks a vehicle before it gives a test +certificate. The check returns one reason code for each vehicle. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_INSPECTION_CHECK exists in package $TMP. It is active. +- The interface has the type of the reason code, one constant for each reason + code, and one method that checks a vehicle. +- Read the interface. It gives the exact method name, the parameters and the + type of the return value. This specification does not repeat the signature. + +# 4. Contract +- Create the class {{P}}INSPECTION_CHECKER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class implements {{P}}IF_INSPECTION_CHECK. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Return the reason code of the first rule that +fails. +1. Remove all spaces from the plate. Convert all letters to upper case. If the + result is empty, return the reason code for a missing plate (NO_PLATE). +2. Remove all spaces from the make. Convert all letters to upper case. If the + result is empty, return the reason code for a missing make (NO_MAKE). +3. If the first registration date is initial or after the check date, return the + reason code for a bad registration date (REG_DATE). +4. Calculate the age of the vehicle. The age is the number of full years between + the first registration date and the check date. If the age is 30 years or + more, return the reason code for an old vehicle (TOO_OLD). +5. If the mileage is less than zero, return the reason code for a bad mileage + (MILEAGE). +6. If the brake performance is less than 50 percent, return the reason code for + weak brakes (BRAKES). +7. If the exhaust value is more than 1000 ppm, return the reason code for a bad + exhaust value (EXHAUST). +8. Else return the reason code for a passed check (OK). + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_INSPECTION_CHECK. + +# 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/G1017/task.json b/tasks_gen/train/G1017/task.json new file mode 100644 index 0000000..da0824f --- /dev/null +++ b/tasks_gen/train/G1017/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1017", + "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_INSPECTION_CHECK", + "file": "seed/if_inspection_check.intf.abap", + "description": "Vehicle inspection check contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}INSPECTION_CHECKER", + "implements": "{{P}}IF_INSPECTION_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_INSPECTION_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_F01_HIDDEN", + "file": "hidden/t_f01_hidden.clas.abap", + "description": "F01 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}INSPECTION_CHECKER", + "file": "reference/inspection_checker.clas.abap", + "description": "Vehicle inspection checker", + "testclasses_file": "reference/inspection_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1018/faulty/m0_ad_price.func.abap b/tasks_gen/train/G1018/faulty/m0_ad_price.func.abap new file mode 100644 index 0000000..d6f2682 --- /dev/null +++ b/tasks_gen/train/G1018/faulty/m0_ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions >= 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/faulty/m1_ad_price.func.abap b/tasks_gen/train/G1018/faulty/m1_ad_price.func.abap new file mode 100644 index 0000000..32101c5 --- /dev/null +++ b/tasks_gen/train/G1018/faulty/m1_ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines / lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/faulty/m2_ad_price.func.abap b/tasks_gen/train/G1018/faulty/m2_ad_price.func.abap new file mode 100644 index 0000000..3b74de2 --- /dev/null +++ b/tasks_gen/train/G1018/faulty/m2_ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo <> abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/faulty/m3_ad_price.func.abap b/tasks_gen/train/G1018/faulty/m3_ad_price.func.abap new file mode 100644 index 0000000..1eb9d5a --- /dev/null +++ b/tasks_gen/train/G1018/faulty/m3_ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 + lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/faulty/m4_ad_price.func.abap b/tasks_gen/train/G1018/faulty/m4_ad_price.func.abap new file mode 100644 index 0000000..41675e1 --- /dev/null +++ b/tasks_gen/train/G1018/faulty/m4_ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 3 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/generation.json b/tasks_gen/train/G1018/generation.json new file mode 100644 index 0000000..a2311c9 --- /dev/null +++ b/tasks_gen/train/G1018/generation.json @@ -0,0 +1,52 @@ +{ + "id": "G1018", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/ad_test.clas.abap line 40: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 40: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "reference/ad_test.clas.abap line 45: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 45: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"80\"", + "reference/ad_test.clas.abap line 50: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 50: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 51: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 51: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "hidden/ad_hidden.clas.abap line 57: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 57: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "hidden/ad_hidden.clas.abap line 63: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 63: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 69: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 69: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 75: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 75: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"82\"", + "hidden/ad_hidden.clas.abap line 81: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 81: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"84\"", + "hidden/ad_hidden.clas.abap line 87: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 87: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"62\"" + ], + "by_check": { + "abaplint": 20 + } + }, + { + "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/G1018/hidden/ad_hidden.clas.abap b/tasks_gen/train/G1018/hidden/ad_hidden.clas.abap new file mode 100644 index 0000000..3a98964 --- /dev/null +++ b/tasks_gen/train/G1018/hidden/ad_hidden.clas.abap @@ -0,0 +1,125 @@ +CLASS {{p}}ad_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS lc_price_2490 TYPE decfloat34 VALUE '24.90'. + CONSTANTS lc_price_4990 TYPE decfloat34 VALUE '49.90'. + CONSTANTS lc_price_2000 TYPE decfloat34 VALUE '20.00'. + CONSTANTS lc_price_4482 TYPE decfloat34 VALUE '44.82'. + CONSTANTS lc_price_3984 TYPE decfloat34 VALUE '39.84'. + CONSTANTS lc_price_3362 TYPE decfloat34 VALUE '33.62'. + CONSTANTS lc_price_6980 TYPE decfloat34 VALUE '69.80'. + METHODS call + IMPORTING iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool DEFAULT abap_false + EXPORTING ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS assert_price + IMPORTING iv_expected TYPE decfloat34 + iv_actual TYPE decfloat34. + METHODS two_lines_one_run FOR TESTING. + METHODS photo_surcharge FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS minimum_after_disc FOR TESTING. + METHODS discount_ten_percent FOR TESTING. + METHODS discount_twenty_pct FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS photo_with_discount FOR TESTING. + METHODS rejects_zero_lines FOR TESTING. + METHODS rejects_zero_runs FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ad_hidden IMPLEMENTATION. + METHOD call. + ev_price = 0. + CALL FUNCTION '{{P}}AD_PRICE' + EXPORTING + iv_lines = iv_lines + iv_insertions = iv_insertions + iv_photo = iv_photo + IMPORTING + ev_price = ev_price + EXCEPTIONS + invalid_lines = 1 + invalid_insertions = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD assert_price. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD two_lines_one_run. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + assert_price( iv_expected = lc_price_2490 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_surcharge. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 2 iv_insertions = 1 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4990 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_charge. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 1 iv_insertions = 1 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_after_disc. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 2 iv_insertions = 6 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_ten_percent. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4482 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_twenty_pct. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 6 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_3984 iv_actual = lv_price ). + ENDMETHOD. + + METHOD rounding_half_up. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 3 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_3362 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_with_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 5 iv_insertions = 6 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_6980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD rejects_zero_lines. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 0 iv_insertions = 1 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD rejects_zero_runs. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 0 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1018/mutation.json b/tasks_gen/train/G1018/mutation.json new file mode 100644 index 0000000..84ce4d5 --- /dev/null +++ b/tasks_gen/train/G1018/mutation.json @@ -0,0 +1,78 @@ +{ + "task": "G1018", + "mutants": [ + { + "object": "{{P}}AD_PRICE", + "mutant": "line 23: < -> >= (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "DISCOUNT_TEN_PERCENT", + "DISCOUNT_TWENTY_PCT", + "MINIMUM_AFTER_DISC", + "MINIMUM_CHARGE", + "PHOTO_SURCHARGE", + "PHOTO_WITH_DISCOUNT", + "REJECTS_ZERO_RUNS", + "ROUNDING_HALF_UP", + "TWO_LINES_ONE_RUN" + ] + }, + { + "object": "{{P}}AD_PRICE", + "mutant": "line 27: * -> / (arith)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "DISCOUNT_TEN_PERCENT", + "DISCOUNT_TWENTY_PCT", + "PHOTO_SURCHARGE", + "PHOTO_WITH_DISCOUNT", + "ROUNDING_HALF_UP", + "TWO_LINES_ONE_RUN" + ] + }, + { + "object": "{{P}}AD_PRICE", + "mutant": "line 28: = -> <> (eq)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "DISCOUNT_TEN_PERCENT", + "DISCOUNT_TWENTY_PCT", + "MINIMUM_AFTER_DISC", + "MINIMUM_CHARGE", + "PHOTO_SURCHARGE", + "PHOTO_WITH_DISCOUNT", + "ROUNDING_HALF_UP", + "TWO_LINES_ONE_RUN" + ] + }, + { + "object": "{{P}}AD_PRICE", + "mutant": "line 39: - -> + (arith)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "DISCOUNT_TEN_PERCENT", + "DISCOUNT_TWENTY_PCT", + "MINIMUM_AFTER_DISC", + "PHOTO_WITH_DISCOUNT", + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}AD_PRICE", + "mutant": "line 40: 2 -> 3 (const)", + "status": "killed", + "hidden": "9/10", + "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/G1018/reference/ad_price.func.abap b/tasks_gen/train/G1018/reference/ad_price.func.abap new file mode 100644 index 0000000..7b2eb72 --- /dev/null +++ b/tasks_gen/train/G1018/reference/ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1018/reference/ad_test.clas.abap b/tasks_gen/train/G1018/reference/ad_test.clas.abap new file mode 100644 index 0000000..c74e444 --- /dev/null +++ b/tasks_gen/train/G1018/reference/ad_test.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ad_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS lc_price_2490 TYPE decfloat34 VALUE '24.90'. + CONSTANTS lc_price_4980 TYPE decfloat34 VALUE '49.80'. + CONSTANTS lc_price_4482 TYPE decfloat34 VALUE '44.82'. + CONSTANTS lc_price_6980 TYPE decfloat34 VALUE '69.80'. + CONSTANTS lc_price_2000 TYPE decfloat34 VALUE '20.00'. + METHODS call + IMPORTING iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool DEFAULT abap_false + EXPORTING ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS assert_price + IMPORTING iv_expected TYPE decfloat34 + iv_actual TYPE decfloat34. + METHODS line_price FOR TESTING. + METHODS no_discount_short FOR TESTING. + METHODS discount_ten_pct FOR TESTING. + METHODS photo_and_discount FOR TESTING. + METHODS minimum_price FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ad_test IMPLEMENTATION. + METHOD call. + ev_price = 0. + CALL FUNCTION '{{P}}AD_PRICE' + EXPORTING + iv_lines = iv_lines + iv_insertions = iv_insertions + iv_photo = iv_photo + IMPORTING + ev_price = ev_price + EXCEPTIONS + invalid_lines = 1 + invalid_insertions = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD assert_price. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD line_price. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + assert_price( iv_expected = lc_price_2490 iv_actual = lv_price ). + ENDMETHOD. + + METHOD no_discount_short. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 2 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_ten_pct. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4482 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_and_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 5 iv_insertions = 6 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_6980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_price. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 1 iv_insertions = 1 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD invalid_input. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 0 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1018/spec.md b/tasks_gen/train/G1018/spec.md new file mode 100644 index 0000000..a3d4da3 --- /dev/null +++ b/tasks_gen/train/G1018/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Calculate the price of a classified advertisement for a newspaper. The sales +desk calls one function module for each advertisement order, before it saves +the order. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_AD exists in package $TMP. +- The sales desk calls the new function module for each advertisement order. + +# 4. Contract +- Create the function module {{P}}AD_PRICE in function group {{P}}FG_AD. +- The interface of {{P}}AD_PRICE is: + - importing iv_lines type i: the number of lines of the advertisement. + - importing iv_insertions type i: the number of times that the advertisement + is printed. + - importing iv_photo type abap_bool: abap_true if the advertisement has a + photo. + - exporting ev_price type decfloat34: the price of the advertisement in euro. + - exceptions invalid_lines and invalid_insertions. + +# 5. Business rules +1. If iv_lines is less than 1, raise the exception INVALID_LINES. +2. If iv_insertions is less than 1, raise the exception INVALID_INSERTIONS. +3. One line of the advertisement costs 12.45 euro. +4. A photo costs 25.00 euro. Charge the photo once for each order. +5. The discount applies to the sum of the line price and the photo price: + - 1 or 2 insertions: no discount. + - 3, 4 or 5 insertions: 10 % discount. + - 6 or more insertions: 20 % discount. +6. Round the price half up to 2 decimals. +7. The minimum price is 20.00 euro. Apply the minimum price after the discount + and after the rounding. + +# 6. Constraints +- Release target: 7.40 SP05. Use only ABAP syntax that release 7.40 SP05 + supports. Do not use inline declarations or constructor expressions. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: database tables, reports, ALV lists, and other function + modules. + +# 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/G1018/task.json b/tasks_gen/train/G1018/task.json new file mode 100644 index 0000000..5e0d796 --- /dev/null +++ b/tasks_gen/train/G1018/task.json @@ -0,0 +1,76 @@ +{ + "id": "G1018", + "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_AD", + "description": "Function group for advertisement pricing" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}AD_PRICE", + "functionGroup": "{{P}}FG_AD", + "params": [ + { + "name": "iv_lines", + "type": "i" + }, + { + "name": "iv_insertions", + "type": "i" + }, + { + "name": "iv_photo", + "type": "abap_bool" + }, + { + "name": "ev_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "database tables", + "reports", + "ALV lists", + "other function modules" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}AD_HIDDEN", + "file": "hidden/ad_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}AD_PRICE", + "functionGroup": "{{P}}FG_AD", + "file": "reference/ad_price.func.abap", + "description": "Advertisement price function module" + }, + { + "type": "CLAS", + "name": "{{P}}AD_TEST", + "file": "reference/ad_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1019/generation.json b/tasks_gen/train/G1019/generation.json new file mode 100644 index 0000000..c88c050 --- /dev/null +++ b/tasks_gen/train/G1019/generation.json @@ -0,0 +1,23 @@ +{ + "id": "G1019", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/bowling_fee.clas.abap line 17: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1019/reference/bowling_fee.clas.abap b/tasks_gen/train/G1019/reference/bowling_fee.clas.abap new file mode 100644 index 0000000..f9ff264 --- /dev/null +++ b/tasks_gen/train/G1019/reference/bowling_fee.clas.abap @@ -0,0 +1,60 @@ +CLASS {{p}}bowling_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_bowling_fee. + + PRIVATE SECTION. + TYPES ty_rate TYPE p LENGTH 5 DECIMALS 2. + + CONSTANTS: + c_lane_fee TYPE {{p}}if_bowling_fee=>ty_fee VALUE '12.00', + c_shoe_fee TYPE {{p}}if_bowling_fee=>ty_fee VALUE '2.50', + c_group_dis TYPE ty_rate VALUE '0.10', + c_large_dis TYPE ty_rate VALUE '0.20'. + + METHODS lane_fee + IMPORTING iv_lanes TYPE i + iv_hours TYPE i + RETURNING VALUE(rv_fee) TYPE {{p}}if_bowling_fee=>ty_fee. + + METHODS discount_rate + IMPORTING iv_lanes TYPE i + iv_hours TYPE i + RETURNING VALUE(rv_rate) TYPE ty_rate. + + METHODS shoe_rental + IMPORTING iv_shoes TYPE i + RETURNING VALUE(rv_fee) TYPE {{p}}if_bowling_fee=>ty_fee. +ENDCLASS. + + +CLASS {{p}}bowling_fee IMPLEMENTATION. + METHOD {{p}}if_bowling_fee~calculate_fee. + DATA(lv_lane_fee) = lane_fee( iv_lanes = iv_lanes iv_hours = iv_hours ). + lv_lane_fee = lv_lane_fee * ( 1 - discount_rate( iv_lanes = iv_lanes iv_hours = iv_hours ) ). + rv_fee = lv_lane_fee + shoe_rental( iv_shoes ). + ENDMETHOD. + + METHOD lane_fee. + IF iv_lanes <= 0 OR iv_hours <= 0. + RETURN. + ENDIF. + rv_fee = c_lane_fee * iv_lanes * iv_hours. + ENDMETHOD. + + METHOD discount_rate. + IF iv_lanes > 4 AND iv_hours > 3. + rv_rate = c_large_dis. + ELSEIF iv_lanes > 4. + rv_rate = c_group_dis. + ELSEIF iv_hours > 3. + rv_rate = c_large_dis. + ENDIF. + ENDMETHOD. + + METHOD shoe_rental. + IF iv_shoes <= 0. + RETURN. + ENDIF. + rv_fee = c_shoe_fee * iv_shoes. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1019/reference/bowling_fee.testclasses.abap b/tasks_gen/train/G1019/reference/bowling_fee.testclasses.abap new file mode 100644 index 0000000..661a1df --- /dev/null +++ b/tasks_gen/train/G1019/reference/bowling_fee.testclasses.abap @@ -0,0 +1,72 @@ +CLASS ltc_bowling_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_48_00 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '48.00', + c_129_60 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '129.60', + c_96_00 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '96.00', + c_288_00 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '288.00', + c_55_50 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '55.50', + c_134_60 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '134.60', + c_0_00 TYPE {{p}}if_bowling_fee=>ty_fee VALUE '0.00'. + + DATA mo_cut TYPE REF TO {{p}}if_bowling_fee. + + METHODS setup. + METHODS lane_fee_only FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS large_discount FOR TESTING. + METHODS group_and_large FOR TESTING. + METHODS shoes_are_added FOR TESTING. + METHODS shoes_never_discounted FOR TESTING. + METHODS zero_lanes FOR TESTING. +ENDCLASS. + + +CLASS ltc_bowling_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}bowling_fee( ). + ENDMETHOD. + + METHOD lane_fee_only. + cl_abap_unit_assert=>assert_equals( + exp = c_48_00 + act = mo_cut->calculate_fee( iv_lanes = 2 iv_hours = 2 iv_shoes = 0 ) ). + ENDMETHOD. + + METHOD group_discount. + cl_abap_unit_assert=>assert_equals( + exp = c_129_60 + act = mo_cut->calculate_fee( iv_lanes = 6 iv_hours = 2 iv_shoes = 0 ) ). + ENDMETHOD. + + METHOD large_discount. + cl_abap_unit_assert=>assert_equals( + exp = c_96_00 + act = mo_cut->calculate_fee( iv_lanes = 2 iv_hours = 5 iv_shoes = 0 ) ). + ENDMETHOD. + + METHOD group_and_large. + " The large discount replaces the group discount. + cl_abap_unit_assert=>assert_equals( + exp = c_288_00 + act = mo_cut->calculate_fee( iv_lanes = 6 iv_hours = 5 iv_shoes = 0 ) ). + ENDMETHOD. + + METHOD shoes_are_added. + cl_abap_unit_assert=>assert_equals( + exp = c_55_50 + act = mo_cut->calculate_fee( iv_lanes = 2 iv_hours = 2 iv_shoes = 3 ) ). + ENDMETHOD. + + METHOD shoes_never_discounted. + cl_abap_unit_assert=>assert_equals( + exp = c_134_60 + act = mo_cut->calculate_fee( iv_lanes = 6 iv_hours = 2 iv_shoes = 2 ) ). + ENDMETHOD. + + METHOD zero_lanes. + cl_abap_unit_assert=>assert_equals( + exp = c_0_00 + act = mo_cut->calculate_fee( iv_lanes = 0 iv_hours = 2 iv_shoes = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1019/seed/if_bowling_fee.intf.abap b/tasks_gen/train/G1019/seed/if_bowling_fee.intf.abap new file mode 100644 index 0000000..79d07dc --- /dev/null +++ b/tasks_gen/train/G1019/seed/if_bowling_fee.intf.abap @@ -0,0 +1,8 @@ +INTERFACE {{p}}if_bowling_fee PUBLIC. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_fee + IMPORTING iv_lanes TYPE i + iv_hours TYPE i + iv_shoes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDINTERFACE. diff --git a/tasks_gen/train/G1019/spec.md b/tasks_gen/train/G1019/spec.md new file mode 100644 index 0000000..12aa4b9 --- /dev/null +++ b/tasks_gen/train/G1019/spec.md @@ -0,0 +1,45 @@ +# 1. Goal +Calculate the fee of a bowling lane booking. A checkout program calls the +calculation before it takes the payment. The program needs one fee for each +booking. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_BOWLING_FEE exists in package $TMP. It is active. +- The interface has the type TY_FEE (P LENGTH 8 DECIMALS 2) and one method: + CALCULATE_FEE IMPORTING iv_lanes TYPE i, iv_hours TYPE i, iv_shoes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_fee. +- The checkout program gives the number of lanes, the number of hours, and the + number of pairs of shoes of the booking. + +# 4. Contract +- Create the class {{P}}BOWLING_FEE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_BOWLING_FEE. +- Do not add other public methods. + +# 5. Business rules +1. The lane fee is 12.00 EUR for each lane and each hour. +2. The shoe rental is 2.50 EUR for each pair of shoes. +3. A booking of more than 4 lanes is a group booking. +4. A booking of more than 3 hours is a large booking. +5. A group booking gets a discount of 10 % on the lane fee. +6. A large booking gets a discount of 20 % on the lane fee. +7. A booking gets exactly one discount. +8. The shoe rental is never discounted. +9. If the number of lanes is zero or less, or if the number of hours is zero or + less, the lane fee is zero. +10. If the number of pairs of shoes is zero or less, the shoe rental is zero. +11. The fee is the lane fee after the discount plus the shoe rental. + +# 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_BOWLING_FEE. + +# 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/G1019/task.json b/tasks_gen/train/G1019/task.json new file mode 100644 index 0000000..6c8d935 --- /dev/null +++ b/tasks_gen/train/G1019/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1019", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "Rules 3 to 7 do not say which discount a booking gets when it is both a group booking and a large booking, although rule 7 says that a booking gets exactly one discount.", + "keywords": [ + [ + "group", + "large" + ], + [ + "discount", + "which" + ], + [ + "booking", + "discount" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_BOWLING_FEE", + "file": "seed/if_bowling_fee.intf.abap", + "description": "Bowling fee contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}BOWLING_FEE", + "implements": "{{P}}IF_BOWLING_FEE" + } + ], + "out_of_scope": [ + "{{P}}IF_BOWLING_FEE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}BOWLING_FEE", + "file": "reference/bowling_fee.clas.abap", + "description": "Bowling fee calculation; the intended reading is that the large discount replaces the group discount", + "testclasses_file": "reference/bowling_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1020/faulty/m0_i_plant_grade.ddls.asddls b/tasks_gen/train/G1020/faulty/m0_i_plant_grade.ddls.asddls new file mode 100644 index 0000000..6143f00 --- /dev/null +++ b/tasks_gen/train/G1020/faulty/m0_i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm >= 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1020/faulty/m1_i_plant_grade.ddls.asddls b/tasks_gen/train/G1020/faulty/m1_i_plant_grade.ddls.asddls new file mode 100644 index 0000000..9e44072 --- /dev/null +++ b/tasks_gen/train/G1020/faulty/m1_i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm >= 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1020/faulty/m3_i_plant_grade.ddls.asddls b/tasks_gen/train/G1020/faulty/m3_i_plant_grade.ddls.asddls new file mode 100644 index 0000000..d17246e --- /dev/null +++ b/tasks_gen/train/G1020/faulty/m3_i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 201 then 'M' + else 'L' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1020/faulty/m4_i_plant_grade.ddls.asddls b/tasks_gen/train/G1020/faulty/m4_i_plant_grade.ddls.asddls new file mode 100644 index 0000000..4e837a0 --- /dev/null +++ b/tasks_gen/train/G1020/faulty/m4_i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'Z' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1020/generation.json b/tasks_gen/train/G1020/generation.json new file mode 100644 index 0000000..713e483 --- /dev/null +++ b/tasks_gen/train/G1020/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1020", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "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/G1020/hidden/t33_hidden.clas.abap b/tasks_gen/train/G1020/hidden/t33_hidden.clas.abap new file mode 100644 index 0000000..c7adebf --- /dev/null +++ b/tasks_gen/train/G1020/hidden/t33_hidden.clas.abap @@ -0,0 +1,86 @@ +CLASS {{p}}t33_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_grade FOR TESTING. + METHODS medium_grade FOR TESTING. + METHODS large_grade FOR TESTING. + METHODS height_in_metres FOR TESTING. + METHODS grade_text_value FOR TESTING. + METHODS row_count FOR TESTING. + METHODS species_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t33_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PLANT_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_plant TYPE STANDARD TABLE OF {{p}}plant WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_plant = VALUE #( ( tree_id = 'T1' species = 'OAK' height_cm = 99 ) + ( tree_id = 'T2' species = 'OAK' height_cm = 150 ) + ( tree_id = 'T3' species = 'PINE' height_cm = 200 ) + ( tree_id = 'T4' species = 'PINE' height_cm = 100 ) + ( tree_id = 'T5' species = 'BIRCH' height_cm = 199 ) + ( tree_id = 'T6' species = 'BIRCH' height_cm = 250 ) ). + go_env->insert_test_data( lt_plant ). + ENDMETHOD. + + METHOD small_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T1' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lv_grade ). + ENDMETHOD. + + METHOD medium_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T2' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T4' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T5' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + ENDMETHOD. + + METHOD large_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T6' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_grade ). + ENDMETHOD. + + METHOD height_in_metres. + SELECT SINGLE height_m FROM {{p}}i_plant_grade WHERE tree_id = 'T2' INTO @DATA(lv_height). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.50' ) + act = CONV decfloat34( lv_height ) ). + ENDMETHOD. + + METHOD grade_text_value. + DATA lv_text TYPE c LENGTH 30. + SELECT SINGLE grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'T1' INTO @lv_text. + cl_abap_unit_assert=>assert_equals( exp = 'S-OAK' act = lv_text ). + SELECT SINGLE grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @lv_text. + cl_abap_unit_assert=>assert_equals( exp = 'L-PINE' act = lv_text ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_plant_grade INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_count ). + ENDMETHOD. + + METHOD species_shown. + SELECT SINGLE species FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @DATA(lv_species). + cl_abap_unit_assert=>assert_equals( exp = 'PINE' act = lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1020/mutation.json b/tasks_gen/train/G1020/mutation.json new file mode 100644 index 0000000..7705169 --- /dev/null +++ b/tasks_gen/train/G1020/mutation.json @@ -0,0 +1,53 @@ +{ + "task": "G1020", + "mutants": [ + { + "object": "{{P}}I_PLANT_GRADE", + "mutant": "line 11: < -> >= (rel)", + "status": "killed", + "hidden": "5/7", + "failed_tests": [ + "LARGE_GRADE", + "MEDIUM_GRADE" + ] + }, + { + "object": "{{P}}I_PLANT_GRADE", + "mutant": "line 15: < -> >= (rel)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "GRADE_TEXT_VALUE" + ] + }, + { + "object": "{{P}}I_PLANT_GRADE", + "mutant": "line 15: 100 -> 101 (const)", + "status": "survived", + "hidden": "7/7", + "failed_tests": [] + }, + { + "object": "{{P}}I_PLANT_GRADE", + "mutant": "line 16: 200 -> 201 (const)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "GRADE_TEXT_VALUE" + ] + }, + { + "object": "{{P}}I_PLANT_GRADE", + "mutant": "line 17: 'L' -> 'Z' (lit)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "GRADE_TEXT_VALUE" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1020/reference/i_plant_grade.ddls.asddls b/tasks_gen/train/G1020/reference/i_plant_grade.ddls.asddls new file mode 100644 index 0000000..d1edce1 --- /dev/null +++ b/tasks_gen/train/G1020/reference/i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1020/reference/t33_test.clas.abap b/tasks_gen/train/G1020/reference/t33_test.clas.abap new file mode 100644 index 0000000..9645671 --- /dev/null +++ b/tasks_gen/train/G1020/reference/t33_test.clas.abap @@ -0,0 +1,43 @@ +CLASS {{p}}t33_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 grade_and_text FOR TESTING. + METHODS height_in_metres FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t33_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PLANT_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_plant TYPE STANDARD TABLE OF {{p}}plant WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_plant = VALUE #( ( tree_id = 'A' species = 'OAK' height_cm = 150 ) ). + go_env->insert_test_data( lt_plant ). + ENDMETHOD. + + METHOD grade_and_text. + DATA lv_grade TYPE c LENGTH 1. + DATA lv_text TYPE c LENGTH 30. + SELECT SINGLE grade, grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'A' + INTO (@lv_grade, @lv_text). + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + cl_abap_unit_assert=>assert_equals( exp = 'M-OAK' act = lv_text ). + ENDMETHOD. + + METHOD height_in_metres. + DATA lv_height TYPE decfloat34. + SELECT SINGLE height_m FROM {{p}}i_plant_grade WHERE tree_id = 'A' INTO @lv_height. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.50' ) act = lv_height ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1020/seed/plant.tabl.asabap b/tasks_gen/train/G1020/seed/plant.tabl.asabap new file mode 100644 index 0000000..e9dce15 --- /dev/null +++ b/tasks_gen/train/G1020/seed/plant.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Tree sapling' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}plant { + key client : abap.clnt not null; + key tree_id : abap.char(10) not null; + species : abap.char(20); + height_cm : abap.int4; +} diff --git a/tasks_gen/train/G1020/spec.md b/tasks_gen/train/G1020/spec.md new file mode 100644 index 0000000..72e2c39 --- /dev/null +++ b/tasks_gen/train/G1020/spec.md @@ -0,0 +1,39 @@ +# 1. Goal +Show the grade of each tree sapling in a plant nursery. A report and an OData +service will read the grades, so the calculation must be in the database, in a +CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}PLANT (tree sapling), package $TMP. Key: TREE_ID (CHAR 10). + Fields: SPECIES (CHAR 20), HEIGHT_CM (INT4, the height in centimetres). + +# 4. Contract +- Create the CDS view entity {{P}}I_PLANT_GRADE in package $TMP. +- Elements, with these names: TREE_ID (key), SPECIES, HEIGHT_M, GRADE, + GRADE_TEXT. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each tree sapling. Do not add rows and do not remove rows. +2. SPECIES is the species of the sapling. +3. HEIGHT_M is the height in metres with 2 decimals. Example: 150 cm is + 1.50 m. +4. GRADE is the size class of the sapling: + - 'S' when the height is less than 100 cm. + - 'M' when the height is 100 cm or more and less than 200 cm. + - 'L' when the height is 200 cm or more. +5. GRADE_TEXT is the grade, then a hyphen, then the species. Example: a pine + sapling of grade 'L' has the text 'L-PINE'. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Use a CDS view entity, not a database view. +- Out of scope: do not change {{P}}PLANT. + +# 7. Acceptance +- The view {{P}}I_PLANT_GRADE 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/G1020/task.json b/tasks_gen/train/G1020/task.json new file mode 100644 index 0000000..75c8064 --- /dev/null +++ b/tasks_gen/train/G1020/task.json @@ -0,0 +1,59 @@ +{ + "id": "G1020", + "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}}PLANT", + "file": "seed/plant.tabl.asabap", + "description": "Tree sapling" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_PLANT_GRADE", + "fields": [ + "TREE_ID", + "SPECIES", + "HEIGHT_M", + "GRADE", + "GRADE_TEXT" + ] + } + ], + "out_of_scope": [ + "{{P}}PLANT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T33_HIDDEN", + "file": "hidden/t33_hidden.clas.abap", + "description": "T33 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_PLANT_GRADE", + "file": "reference/i_plant_grade.ddls.asddls", + "description": "Grade of each tree sapling" + }, + { + "type": "CLAS", + "name": "{{P}}T33_TEST", + "file": "reference/t33_test.clas.abap", + "description": "T33 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1021/generation.json b/tasks_gen/train/G1021/generation.json new file mode 100644 index 0000000..68a71e2 --- /dev/null +++ b/tasks_gen/train/G1021/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1021", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "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/G1021/hidden/t41_hidden.clas.abap b/tasks_gen/train/G1021/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..ca0adf5 --- /dev/null +++ b/tasks_gen/train/G1021/hidden/t41_hidden.clas.abap @@ -0,0 +1,235 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}roast_merger. + + METHODS setup. + METHODS weight_of + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_weight) TYPE {{p}}roast_merger=>ty_weight. + METHODS total_of + IMPORTING it_totals TYPE {{p}}roast_merger=>tt_lot_totals + iv_lot TYPE {{p}}roast_merger=>ty_lot_id + RETURNING VALUE(rs_total) TYPE {{p}}roast_merger=>ty_lot_total. + + METHODS merges_two_plants FOR TESTING. + METHODS result_sorted_by_lot FOR TESTING. + METHODS dedup_keeps_larger FOR TESTING. + METHODS dedup_keeps_first FOR TESTING. + METHODS dedup_within_one_plant FOR TESTING. + METHODS last_date_is_latest FOR TESTING. + METHODS ignores_zero_weight FOR TESTING. + METHODS ignores_empty_batch_id FOR TESTING. + METHODS empty_input_gives_empty FOR TESTING. + METHODS lot_with_only_invalid FOR TESTING. + METHODS single_batch FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}roast_merger( ). + ENDMETHOD. + + METHOD weight_of. + rv_weight = iv_text. + ENDMETHOD. + + METHOD total_of. + READ TABLE it_totals WITH KEY lot_id = iv_lot INTO rs_total. + ENDMETHOD. + + METHOD merges_two_plants. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240110' roasted_kg = '100.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT002' + roast_date = '20240111' roasted_kg = '50.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B3' plant = 'P002' lot_id = 'LOT002' + roast_date = '20240112' roasted_kg = '25.00' ) + ( batch_id = 'B4' plant = 'P002' lot_id = 'LOT003' + roast_date = '20240113' roasted_kg = '10.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 3 ). + + DATA(ls_lot1) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot1-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot1-total_kg exp = weight_of( '100.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot1-last_date exp = '20240110' ). + + DATA(ls_lot2) = total_of( it_totals = lt_totals iv_lot = 'LOT002' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-batch_count exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-total_kg exp = weight_of( '75.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-last_date exp = '20240112' ). + + DATA(ls_lot3) = total_of( it_totals = lt_totals iv_lot = 'LOT003' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot3-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot3-total_kg exp = weight_of( '10.00' ) ). + ENDMETHOD. + + METHOD result_sorted_by_lot. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT300' + roast_date = '20240101' roasted_kg = '10.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT100' + roast_date = '20240102' roasted_kg = '20.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B3' plant = 'P002' lot_id = 'LOT200' + roast_date = '20240103' roasted_kg = '30.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 3 ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 1 ]-lot_id exp = 'LOT100' ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 2 ]-lot_id exp = 'LOT200' ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 3 ]-lot_id exp = 'LOT300' ). + ENDMETHOD. + + METHOD dedup_keeps_larger. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '40.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P002' lot_id = 'LOT001' + roast_date = '20240102' roasted_kg = '60.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '60.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240102' ). + ENDMETHOD. + + METHOD dedup_keeps_first. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240105' roasted_kg = '70.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P002' lot_id = 'LOT001' + roast_date = '20240106' roasted_kg = '30.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '70.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240105' ). + ENDMETHOD. + + METHOD dedup_within_one_plant. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '20.00' ) + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240102' roasted_kg = '35.00' ) + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240103' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '35.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240102' ). + ENDMETHOD. + + METHOD last_date_is_latest. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240120' roasted_kg = '10.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240105' roasted_kg = '10.00' ) + ( batch_id = 'B3' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240131' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 3 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '30.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240131' ). + ENDMETHOD. + + METHOD ignores_zero_weight. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '0.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240102' roasted_kg = '10.00' ) + ( batch_id = 'B3' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240103' roasted_kg = '-5.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '10.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240102' ). + ENDMETHOD. + + METHOD ignores_empty_batch_id. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = '' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '50.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240102' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '10.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240102' ). + ENDMETHOD. + + METHOD empty_input_gives_empty. + DATA lt_empty TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_empty it_plant_b = lt_empty ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 0 ). + ENDMETHOD. + + METHOD lot_with_only_invalid. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '0.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT002' + roast_date = '20240102' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 1 ]-lot_id exp = 'LOT002' ). + ENDMETHOD. + + METHOD single_batch. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240107' roasted_kg = '12.50' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '12.50' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240107' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1021/reference/roast_merger.clas.abap b/tasks_gen/train/G1021/reference/roast_merger.clas.abap new file mode 100644 index 0000000..7ece2cf --- /dev/null +++ b/tasks_gen/train/G1021/reference/roast_merger.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}roast_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_batch_id TYPE c LENGTH 10, + ty_plant TYPE c LENGTH 4, + ty_lot_id TYPE c LENGTH 8, + ty_weight TYPE p LENGTH 7 DECIMALS 2, + BEGIN OF ty_batch, + batch_id TYPE ty_batch_id, + plant TYPE ty_plant, + lot_id TYPE ty_lot_id, + roast_date TYPE d, + roasted_kg TYPE ty_weight, + END OF ty_batch, + tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY, + BEGIN OF ty_lot_total, + lot_id TYPE ty_lot_id, + batch_count TYPE i, + total_kg TYPE ty_weight, + last_date TYPE d, + END OF ty_lot_total, + tt_lot_totals TYPE STANDARD TABLE OF ty_lot_total WITH EMPTY KEY. + + METHODS merge + IMPORTING it_plant_a TYPE tt_batches + it_plant_b TYPE tt_batches + RETURNING VALUE(rt_totals) TYPE tt_lot_totals. + + PRIVATE SECTION. + TYPES tt_unique TYPE HASHED TABLE OF ty_batch WITH UNIQUE KEY batch_id. + + METHODS add_batches + IMPORTING it_batches TYPE tt_batches + CHANGING ct_unique TYPE tt_unique. + + METHODS build_totals + IMPORTING it_unique TYPE tt_unique + RETURNING VALUE(rt_totals) TYPE tt_lot_totals. +ENDCLASS. + + +CLASS {{p}}roast_merger IMPLEMENTATION. + METHOD merge. + DATA lt_unique TYPE tt_unique. + add_batches( EXPORTING it_batches = it_plant_a + CHANGING ct_unique = lt_unique ). + add_batches( EXPORTING it_batches = it_plant_b + CHANGING ct_unique = lt_unique ). + rt_totals = build_totals( lt_unique ). + ENDMETHOD. + + METHOD add_batches. + LOOP AT it_batches INTO DATA(ls_batch). + IF ls_batch-batch_id IS INITIAL OR ls_batch-roasted_kg <= 0. + CONTINUE. + ENDIF. + READ TABLE ct_unique WITH TABLE KEY batch_id = ls_batch-batch_id + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_batch INTO TABLE ct_unique. + ELSEIF ls_batch-roasted_kg > -roasted_kg. + = ls_batch. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD build_totals. + DATA lt_totals TYPE SORTED TABLE OF ty_lot_total WITH UNIQUE KEY lot_id. + LOOP AT it_unique INTO DATA(ls_batch). + READ TABLE lt_totals WITH TABLE KEY lot_id = ls_batch-lot_id + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( lot_id = ls_batch-lot_id + batch_count = 1 + total_kg = ls_batch-roasted_kg + last_date = ls_batch-roast_date ) + INTO TABLE lt_totals. + CONTINUE. + ENDIF. + -batch_count = -batch_count + 1. + -total_kg = -total_kg + ls_batch-roasted_kg. + IF ls_batch-roast_date > -last_date. + -last_date = ls_batch-roast_date. + ENDIF. + ENDLOOP. + rt_totals = lt_totals. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1021/reference/roast_merger.testclasses.abap b/tasks_gen/train/G1021/reference/roast_merger.testclasses.abap new file mode 100644 index 0000000..ac1a4c1 --- /dev/null +++ b/tasks_gen/train/G1021/reference/roast_merger.testclasses.abap @@ -0,0 +1,147 @@ +CLASS ltc_roast_merger DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}roast_merger. + + METHODS setup. + METHODS weight_of + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_weight) TYPE {{p}}roast_merger=>ty_weight. + METHODS total_of + IMPORTING it_totals TYPE {{p}}roast_merger=>tt_lot_totals + iv_lot TYPE {{p}}roast_merger=>ty_lot_id + RETURNING VALUE(rs_total) TYPE {{p}}roast_merger=>ty_lot_total. + + METHODS merges_two_plants FOR TESTING. + METHODS keeps_larger_weight FOR TESTING. + METHODS ignores_invalid FOR TESTING. + METHODS sorts_by_lot FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS last_date_is_latest FOR TESTING. + METHODS single_batch FOR TESTING. +ENDCLASS. + + +CLASS ltc_roast_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}roast_merger( ). + ENDMETHOD. + + METHOD weight_of. + rv_weight = iv_text. + ENDMETHOD. + + METHOD total_of. + READ TABLE it_totals WITH KEY lot_id = iv_lot INTO rs_total. + ENDMETHOD. + + METHOD merges_two_plants. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240110' roasted_kg = '100.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT002' + roast_date = '20240111' roasted_kg = '50.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B3' plant = 'P002' lot_id = 'LOT002' + roast_date = '20240112' roasted_kg = '25.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 2 ). + DATA(ls_lot2) = total_of( it_totals = lt_totals iv_lot = 'LOT002' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-batch_count exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-total_kg exp = weight_of( '75.00' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot2-last_date exp = '20240112' ). + ENDMETHOD. + + METHOD keeps_larger_weight. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '40.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P002' lot_id = 'LOT001' + roast_date = '20240102' roasted_kg = '60.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '60.00' ) ). + ENDMETHOD. + + METHOD ignores_invalid. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = '' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240101' roasted_kg = '50.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT002' + roast_date = '20240102' roasted_kg = '0.00' ) + ( batch_id = 'B3' plant = 'P001' lot_id = 'LOT002' + roast_date = '20240103' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT002' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '10.00' ) ). + ENDMETHOD. + + METHOD sorts_by_lot. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT300' + roast_date = '20240101' roasted_kg = '10.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT100' + roast_date = '20240102' roasted_kg = '20.00' ) ). + DATA(lt_b) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B3' plant = 'P002' lot_id = 'LOT200' + roast_date = '20240103' roasted_kg = '30.00' ) ). + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 3 ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 1 ]-lot_id exp = 'LOT100' ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 2 ]-lot_id exp = 'LOT200' ). + cl_abap_unit_assert=>assert_equals( act = lt_totals[ 3 ]-lot_id exp = 'LOT300' ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_empty TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_empty it_plant_b = lt_empty ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 0 ). + ENDMETHOD. + + METHOD last_date_is_latest. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240120' roasted_kg = '10.00' ) + ( batch_id = 'B2' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240105' roasted_kg = '10.00' ) + ( batch_id = 'B3' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240131' roasted_kg = '10.00' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240131' ). + ENDMETHOD. + + METHOD single_batch. + DATA(lt_a) = VALUE {{p}}roast_merger=>tt_batches( + ( batch_id = 'B1' plant = 'P001' lot_id = 'LOT001' + roast_date = '20240107' roasted_kg = '12.50' ) ). + DATA lt_b TYPE {{p}}roast_merger=>tt_batches. + + DATA(lt_totals) = mo_cut->merge( it_plant_a = lt_a it_plant_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_totals ) exp = 1 ). + DATA(ls_lot) = total_of( it_totals = lt_totals iv_lot = 'LOT001' ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-batch_count exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-total_kg exp = weight_of( '12.50' ) ). + cl_abap_unit_assert=>assert_equals( act = ls_lot-last_date exp = '20240107' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1021/spec.md b/tasks_gen/train/G1021/spec.md new file mode 100644 index 0000000..722ae2f --- /dev/null +++ b/tasks_gen/train/G1021/spec.md @@ -0,0 +1,65 @@ +# 1. Goal +A coffee roastery operates two roasting plants. Each plant sends a list of roast +batches. The roastery needs one consolidated list. The list shows the totals of +each green coffee lot. A planning program calls the consolidation before it +plans the next roasting week. + +# 2. Open questions +None. + +# 3. Context +- Plant A and plant B record their roast batches independently. The same batch + ID can occur in both lists. A batch ID can also occur more than one time in + one list. +- A batch has a batch ID, a plant, a lot ID, a roast date, and a roasted weight + in kilograms. The roasted weight has two decimal places. + +# 4. Contract +- Create the class {{P}}ROAST_MERGER in package $TMP. +- The class is public and final. The class has a public constructor without + parameters. +- The class has these public types: + - TY_BATCH_ID (character, length 10) + - TY_PLANT (character, length 4) + - TY_LOT_ID (character, length 8) + - TY_WEIGHT (packed number, length 7, 2 decimal places) + - TY_BATCH (structure): BATCH_ID, PLANT, LOT_ID, ROAST_DATE (date), + ROASTED_KG (TY_WEIGHT) + - TT_BATCHES: standard table of TY_BATCH with empty key + - TY_LOT_TOTAL (structure): LOT_ID, BATCH_COUNT (integer), TOTAL_KG + (TY_WEIGHT), LAST_DATE (date) + - TT_LOT_TOTALS: standard table of TY_LOT_TOTAL with empty key +- The class has this public method and no other public method: + MERGE + IMPORTING it_plant_a TYPE tt_batches + it_plant_b TYPE tt_batches + RETURNING VALUE(rt_totals) TYPE tt_lot_totals + +# 5. Business rules +The method MERGE applies these rules in this sequence. +1. A batch is invalid if the batch ID is empty, or if the roasted weight is zero + or negative. Ignore all invalid batches. +2. A batch ID identifies a batch. If the same batch ID occurs more than one + time, keep the first occurrence. Replace the kept occurrence only if a later + occurrence has a larger roasted weight. The order of the occurrences is: all + batches of plant A first, then all batches of plant B. +3. Group the remaining batches by lot ID. +4. For each lot ID calculate: + - BATCH_COUNT: the number of batches with this lot ID. + - TOTAL_KG: the sum of the roasted weights of these batches. + - LAST_DATE: the latest roast date of these batches. +5. The result has one row for each lot ID with at least one batch. +6. The result is sorted by LOT_ID in ascending order. +7. If both input tables are empty, the result is empty. + +# 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, + no change of the public contract. + +# 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/G1021/task.json b/tasks_gen/train/G1021/task.json new file mode 100644 index 0000000..cd9833d --- /dev/null +++ b/tasks_gen/train/G1021/task.json @@ -0,0 +1,49 @@ +{ + "id": "G1021", + "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}}ROAST_MERGER" + } + ], + "out_of_scope": [ + "Database tables", + "CDS views", + "Reports", + "Function modules", + "Other public methods of {{P}}ROAST_MERGER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests for the roast batch consolidation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}ROAST_MERGER", + "file": "reference/roast_merger.clas.abap", + "description": "Roast batch consolidator", + "testclasses_file": "reference/roast_merger.testclasses.abap" + } + ], + "craft_checks": [ + "internal_table_kind", + "table_key", + "access_path", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1022/faulty/m0_sweep_pricer.clas.abap b/tasks_gen/train/G1022/faulty/m0_sweep_pricer.clas.abap new file mode 100644 index 0000000..d4e5326 --- /dev/null +++ b/tasks_gen/train/G1022/faulty/m0_sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee / soot_pct( iv_soot ) / 100. + + IF iv_express = abap_true. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues * iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot >= c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 1 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/faulty/m1_sweep_pricer.clas.abap b/tasks_gen/train/G1022/faulty/m1_sweep_pricer.clas.abap new file mode 100644 index 0000000..bdf71fe --- /dev/null +++ b/tasks_gen/train/G1022/faulty/m1_sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee * soot_pct( iv_soot ) / 100. + + IF iv_express = abap_false. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues * iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot >= c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 1 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/faulty/m2_sweep_pricer.clas.abap b/tasks_gen/train/G1022/faulty/m2_sweep_pricer.clas.abap new file mode 100644 index 0000000..0d84546 --- /dev/null +++ b/tasks_gen/train/G1022/faulty/m2_sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee * soot_pct( iv_soot ) / 100. + + IF iv_express = abap_true. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues / iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot >= c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 1 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/faulty/m3_sweep_pricer.clas.abap b/tasks_gen/train/G1022/faulty/m3_sweep_pricer.clas.abap new file mode 100644 index 0000000..cd9038b --- /dev/null +++ b/tasks_gen/train/G1022/faulty/m3_sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee * soot_pct( iv_soot ) / 100. + + IF iv_express = abap_true. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues * iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot < c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 1 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/faulty/m4_sweep_pricer.clas.abap b/tasks_gen/train/G1022/faulty/m4_sweep_pricer.clas.abap new file mode 100644 index 0000000..b79d83f --- /dev/null +++ b/tasks_gen/train/G1022/faulty/m4_sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee * soot_pct( iv_soot ) / 100. + + IF iv_express = abap_true. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues * iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot >= c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 2 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/generation.json b/tasks_gen/train/G1022/generation.json new file mode 100644 index 0000000..58287ad --- /dev/null +++ b/tasks_gen/train/G1022/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1022", + "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/G1022/hidden/sweep_hidden.clas.abap b/tasks_gen/train/G1022/hidden/sweep_hidden.clas.abap new file mode 100644 index 0000000..787c6ed --- /dev/null +++ b/tasks_gen/train/G1022/hidden/sweep_hidden.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}sweep_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}}sweep_pricer. + + METHODS setup. + METHODS min_fee_house FOR TESTING. + METHODS min_fee_apartment FOR TESTING. + METHODS soot_three_surcharge FOR TESTING. + METHODS soot_five_surcharge FOR TESTING. + METHODS volume_discount FOR TESTING. + METHODS discount_with_soot FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS express_over_minimum FOR TESTING. + METHODS unknown_building_zero FOR TESTING. + METHODS no_flues_zero FOR TESTING. + METHODS invalid_soot_no_surcharge FOR TESTING. +ENDCLASS. + + + +CLASS {{p}}sweep_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}sweep_pricer( ). + ENDMETHOD. + + METHOD min_fee_house. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '60.00' ) + act = mo_cut->calculate( iv_flues = 1 + iv_building = 'H' + iv_soot = 1 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD min_fee_apartment. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '60.00' ) + act = mo_cut->calculate( iv_flues = 1 + iv_building = 'A' + iv_soot = 1 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD soot_three_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '99.00' ) + act = mo_cut->calculate( iv_flues = 2 + iv_building = 'H' + iv_soot = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD soot_five_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '216.00' ) + act = mo_cut->calculate( iv_flues = 3 + iv_building = 'R' + iv_soot = 5 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD volume_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '132.30' ) + act = mo_cut->calculate( iv_flues = 4 + iv_building = 'A' + iv_soot = 2 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD discount_with_soot. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '186.30' ) + act = mo_cut->calculate( iv_flues = 4 + iv_building = 'H' + iv_soot = 4 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD express_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '115.00' ) + act = mo_cut->calculate( iv_flues = 2 + iv_building = 'H' + iv_soot = 1 + iv_express = abap_true ) ). + ENDMETHOD. + + METHOD express_over_minimum. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '67.00' ) + act = mo_cut->calculate( iv_flues = 1 + iv_building = 'A' + iv_soot = 5 + iv_express = abap_true ) ). + ENDMETHOD. + + METHOD unknown_building_zero. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '0.00' ) + act = mo_cut->calculate( iv_flues = 3 + iv_building = 'X' + iv_soot = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD no_flues_zero. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '0.00' ) + act = mo_cut->calculate( iv_flues = 0 + iv_building = 'H' + iv_soot = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD invalid_soot_no_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '90.00' ) + act = mo_cut->calculate( iv_flues = 2 + iv_building = 'H' + iv_soot = 0 + iv_express = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/mutation.json b/tasks_gen/train/G1022/mutation.json new file mode 100644 index 0000000..921adcf --- /dev/null +++ b/tasks_gen/train/G1022/mutation.json @@ -0,0 +1,84 @@ +{ + "task": "G1022", + "mutants": [ + { + "object": "{{P}}SWEEP_PRICER", + "mutant": "line 66: * -> / (arith)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "DISCOUNT_WITH_SOOT", + "EXPRESS_OVER_MINIMUM", + "EXPRESS_SURCHARGE", + "INVALID_SOOT_NO_SURCHARGE", + "MIN_FEE_APARTMENT", + "MIN_FEE_HOUSE", + "SOOT_FIVE_SURCHARGE", + "SOOT_THREE_SURCHARGE", + "VOLUME_DISCOUNT" + ] + }, + { + "object": "{{P}}SWEEP_PRICER", + "mutant": "line 68: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "DISCOUNT_WITH_SOOT", + "EXPRESS_OVER_MINIMUM", + "EXPRESS_SURCHARGE", + "INVALID_SOOT_NO_SURCHARGE", + "MIN_FEE_HOUSE", + "SOOT_FIVE_SURCHARGE", + "SOOT_THREE_SURCHARGE", + "VOLUME_DISCOUNT" + ] + }, + { + "object": "{{P}}SWEEP_PRICER", + "mutant": "line 93: * -> / (arith)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "DISCOUNT_WITH_SOOT", + "EXPRESS_OVER_MINIMUM", + "EXPRESS_SURCHARGE", + "INVALID_SOOT_NO_SURCHARGE", + "SOOT_FIVE_SURCHARGE", + "SOOT_THREE_SURCHARGE", + "VOLUME_DISCOUNT" + ] + }, + { + "object": "{{P}}SWEEP_PRICER", + "mutant": "line 100: >= -> < (rel)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "DISCOUNT_WITH_SOOT", + "EXPRESS_OVER_MINIMUM", + "INVALID_SOOT_NO_SURCHARGE", + "SOOT_FIVE_SURCHARGE", + "SOOT_THREE_SURCHARGE", + "VOLUME_DISCOUNT" + ] + }, + { + "object": "{{P}}SWEEP_PRICER", + "mutant": "line 101: 1 -> 2 (const)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "DISCOUNT_WITH_SOOT", + "EXPRESS_OVER_MINIMUM", + "SOOT_FIVE_SURCHARGE", + "SOOT_THREE_SURCHARGE", + "VOLUME_DISCOUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1022/reference/sweep_pricer.clas.abap b/tasks_gen/train/G1022/reference/sweep_pricer.clas.abap new file mode 100644 index 0000000..0f76f1e --- /dev/null +++ b/tasks_gen/train/G1022/reference/sweep_pricer.clas.abap @@ -0,0 +1,106 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_building, + house TYPE ty_building VALUE 'H', + apartment TYPE ty_building VALUE 'A', + restaurant TYPE ty_building VALUE 'R', + END OF c_building, + c_rate_house TYPE ty_fee VALUE '45.00', + c_rate_apartment TYPE ty_fee VALUE '35.00', + c_rate_restaurant TYPE ty_fee VALUE '60.00', + c_express_fee TYPE ty_fee VALUE '25.00', + c_min_fee TYPE ty_fee VALUE '60.00', + c_no_fee TYPE ty_fee VALUE '0.00', + c_discount_pct TYPE i VALUE 10, + c_discount_flues TYPE i VALUE 4, + c_soot_min TYPE i VALUE 2, + c_soot_max TYPE i VALUE 5, + c_soot_step TYPE i VALUE 5. + + METHODS rate_of + IMPORTING iv_building TYPE ty_building + RETURNING VALUE(rv_rate) TYPE ty_fee. + METHODS base_fee + IMPORTING iv_flues TYPE i + iv_rate TYPE ty_fee + RETURNING VALUE(rv_base) TYPE ty_fee. + METHODS soot_pct + IMPORTING iv_soot TYPE i + RETURNING VALUE(rv_pct) TYPE i. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_flues < 1. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_rate) = rate_of( iv_building ). + IF lv_rate IS INITIAL. + rv_fee = c_no_fee. + RETURN. + ENDIF. + + DATA(lv_fee) = base_fee( iv_flues = iv_flues + iv_rate = lv_rate ). + lv_fee = lv_fee + lv_fee * soot_pct( iv_soot ) / 100. + + IF iv_express = abap_true. + lv_fee = lv_fee + c_express_fee. + ENDIF. + + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rv_fee = lv_fee. + ENDMETHOD. + + METHOD rate_of. + CASE iv_building. + WHEN c_building-house. + rv_rate = c_rate_house. + WHEN c_building-apartment. + rv_rate = c_rate_apartment. + WHEN c_building-restaurant. + rv_rate = c_rate_restaurant. + WHEN OTHERS. + rv_rate = c_no_fee. + ENDCASE. + ENDMETHOD. + + METHOD base_fee. + rv_base = iv_flues * iv_rate. + IF iv_flues >= c_discount_flues. + rv_base = rv_base - rv_base * c_discount_pct / 100. + ENDIF. + ENDMETHOD. + + METHOD soot_pct. + IF iv_soot >= c_soot_min AND iv_soot <= c_soot_max. + rv_pct = ( iv_soot - 1 ) * c_soot_step. + ELSE. + rv_pct = 0. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/reference/sweep_pricer.testclasses.abap b/tasks_gen/train/G1022/reference/sweep_pricer.testclasses.abap new file mode 100644 index 0000000..9ba84f9 --- /dev/null +++ b/tasks_gen/train/G1022/reference/sweep_pricer.testclasses.abap @@ -0,0 +1,81 @@ +CLASS ltc_sweep_pricer 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}}sweep_pricer. + + METHODS setup. + METHODS minimum_fee_applies FOR TESTING. + METHODS soot_surcharge FOR TESTING. + METHODS volume_discount FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS unknown_building FOR TESTING. + METHODS invalid_flues FOR TESTING. +ENDCLASS. + + + +CLASS ltc_sweep_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}sweep_pricer( ). + ENDMETHOD. + + METHOD minimum_fee_applies. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '60.00' ) + act = mo_cut->calculate( iv_flues = 1 + iv_building = 'A' + iv_soot = 1 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD soot_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '216.00' ) + act = mo_cut->calculate( iv_flues = 3 + iv_building = 'R' + iv_soot = 5 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD volume_discount. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '132.30' ) + act = mo_cut->calculate( iv_flues = 4 + iv_building = 'A' + iv_soot = 2 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '115.00' ) + act = mo_cut->calculate( iv_flues = 2 + iv_building = 'H' + iv_soot = 1 + iv_express = abap_true ) ). + ENDMETHOD. + + METHOD unknown_building. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '0.00' ) + act = mo_cut->calculate( iv_flues = 3 + iv_building = 'X' + iv_soot = 3 + iv_express = abap_false ) ). + ENDMETHOD. + + METHOD invalid_flues. + cl_abap_unit_assert=>assert_equals( + exp = CONV ty_fee( '0.00' ) + act = mo_cut->calculate( iv_flues = 0 + iv_building = 'H' + iv_soot = 3 + iv_express = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/seed/sweep_pricer.clas.abap b/tasks_gen/train/G1022/seed/sweep_pricer.clas.abap new file mode 100644 index 0000000..2375915 --- /dev/null +++ b/tasks_gen/train/G1022/seed/sweep_pricer.clas.abap @@ -0,0 +1,98 @@ +CLASS {{p}}sweep_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES: + ty_building TYPE c LENGTH 1, + ty_fee TYPE p LENGTH 8 DECIMALS 2. + + METHODS calculate + IMPORTING iv_flues TYPE i + iv_building TYPE ty_building + iv_soot TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + + +CLASS {{p}}sweep_pricer IMPLEMENTATION. + METHOD calculate. + DATA lv_rate TYPE ty_fee. + DATA lv_fee TYPE ty_fee. + DATA lv_pct TYPE i. + DATA lv_i TYPE i. + + rv_fee = 0. + + IF iv_flues > 0. + + IF iv_building = 'H'. + lv_rate = '45.00'. + ELSE. + IF iv_building = 'A'. + lv_rate = '35.00'. + ELSE. + IF iv_building = 'R'. + lv_rate = '60.00'. + ELSE. + lv_rate = 0. + ENDIF. + ENDIF. + ENDIF. + + IF lv_rate > 0. + + lv_fee = 0. + lv_i = 0. + WHILE lv_i < iv_flues. + lv_fee = lv_fee + lv_rate. + lv_i = lv_i + 1. + ENDWHILE. + + IF iv_flues >= 4. + lv_fee = lv_fee - lv_fee * 10 / 100. + ENDIF. + + IF iv_soot = 1. + lv_pct = 0. + ELSE. + IF iv_soot = 2. + lv_pct = 5. + ELSE. + IF iv_soot = 3. + lv_pct = 10. + ELSE. + IF iv_soot = 4. + lv_pct = 15. + ELSE. + IF iv_soot = 5. + lv_pct = 20. + ELSE. + lv_pct = 0. + ENDIF. + ENDIF. + ENDIF. + ENDIF. + ENDIF. + + lv_fee = lv_fee + lv_fee * lv_pct / 100. + + IF iv_express = 'X'. + lv_fee = lv_fee + 25. + ENDIF. + + IF lv_fee < 60. + lv_fee = 60. + ENDIF. + + rv_fee = lv_fee. + + ENDIF. + + ENDIF. + + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1022/spec.md b/tasks_gen/train/G1022/spec.md new file mode 100644 index 0000000..20fbe67 --- /dev/null +++ b/tasks_gen/train/G1022/spec.md @@ -0,0 +1,42 @@ +# 1. Goal +A chimney sweep company calculates the fee of a sweep visit. A dispatcher +program calls the class {{P}}SWEEP_PRICER before it confirms an appointment. +The class works, but the code is hard to read and hard to change. Refactor the +class. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SWEEP_PRICER exists in package $TMP. It is active. +- The class is public and final. It has one public method, CALCULATE. +- The method CALCULATE has the importing parameters iv_flues (TYPE i), + iv_building (TYPE ty_building), iv_soot (TYPE i) and iv_express + (TYPE abap_bool). It has the returning parameter rv_fee (TYPE ty_fee). +- ty_building and ty_fee are public types of the class. ty_building is CHAR 1. + ty_fee is a packed number with 8 characters and 2 decimal places. +- The legacy code contains the complete business logic. Read the legacy code. + +# 4. Contract +- Refactor the class {{P}}SWEEP_PRICER in package $TMP. +- The class stays public and final. +- The public method CALCULATE keeps its name, its parameters and the types of + the parameters. +- Do not add other public methods or public attributes. + +# 5. Business rules +- The legacy code contains all the business rules. +- The behavior of the method CALCULATE stays the same for every input. +- Do not change any result of the method CALCULATE. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. A method has less than 40 statements. No deep + nesting. No magic numbers, use constants. Use meaningful names. No comment + that restates the code. No global variables. +- Out of scope: do not change the public interface of the class. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. They check the unchanged behavior. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1022/task.json b/tasks_gen/train/G1022/task.json new file mode 100644 index 0000000..00b43a7 --- /dev/null +++ b/tasks_gen/train/G1022/task.json @@ -0,0 +1,49 @@ +{ + "id": "G1022", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}SWEEP_PRICER", + "file": "seed/sweep_pricer.clas.abap", + "description": "Legacy chimney sweep fee calculator with one long, deeply nested method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SWEEP_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SWEEP_HIDDEN", + "file": "hidden/sweep_hidden.clas.abap", + "description": "Hidden tests for the unchanged sweep fee behavior" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SWEEP_PRICER", + "file": "reference/sweep_pricer.clas.abap", + "description": "Refactored chimney sweep fee calculator", + "testclasses_file": "reference/sweep_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "magic_numbers", + "deep_nesting" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1023/faulty/m0_rideshare_split.clas.abap b/tasks_gen/train/G1023/faulty/m0_rideshare_split.clas.abap new file mode 100644 index 0000000..42ab65e --- /dev/null +++ b/tasks_gen/train/G1023/faulty/m0_rideshare_split.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}rideshare_split DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_distance TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate_total + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS calculate_share + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_base_fee TYPE ty_amount VALUE '3.00', + c_fee_per_km TYPE ty_amount VALUE '0.40', + c_detour_fee TYPE ty_amount VALUE '1.20', + c_min_share TYPE ty_amount VALUE '1.50'. + + METHODS distance_fee + IMPORTING iv_distance_km TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS detour_fee + IMPORTING iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}rideshare_split IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_total. + IF iv_passengers < 1. + RETURN. + ENDIF. + rv_total = c_base_fee - distance_fee( iv_distance_km ) + detour_fee( iv_passengers ). + ENDMETHOD. + + METHOD calculate_share. + IF iv_passengers < 1. + RETURN. + ENDIF. + DATA(lv_total) = calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ). + DATA lv_share TYPE p LENGTH 15 DECIMALS 5. + lv_share = lv_total / iv_passengers. + rv_share = round( val = lv_share dec = 2 ). + IF rv_share < c_min_share. + rv_share = c_min_share. + ENDIF. + ENDMETHOD. + + METHOD distance_fee. + IF iv_distance_km > 0. + rv_fee = c_fee_per_km * iv_distance_km. + ENDIF. + ENDMETHOD. + + METHOD detour_fee. + rv_fee = c_detour_fee * ( iv_passengers - 1 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/faulty/m1_rideshare_split.clas.abap b/tasks_gen/train/G1023/faulty/m1_rideshare_split.clas.abap new file mode 100644 index 0000000..dc41a0b --- /dev/null +++ b/tasks_gen/train/G1023/faulty/m1_rideshare_split.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}rideshare_split DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_distance TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate_total + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS calculate_share + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_base_fee TYPE ty_amount VALUE '3.00', + c_fee_per_km TYPE ty_amount VALUE '0.40', + c_detour_fee TYPE ty_amount VALUE '1.20', + c_min_share TYPE ty_amount VALUE '1.50'. + + METHODS distance_fee + IMPORTING iv_distance_km TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS detour_fee + IMPORTING iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}rideshare_split IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_total. + IF iv_passengers < 1. + RETURN. + ENDIF. + rv_total = c_base_fee + distance_fee( iv_distance_km ) + detour_fee( iv_passengers ). + ENDMETHOD. + + METHOD calculate_share. + IF iv_passengers >= 1. + RETURN. + ENDIF. + DATA(lv_total) = calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ). + DATA lv_share TYPE p LENGTH 15 DECIMALS 5. + lv_share = lv_total / iv_passengers. + rv_share = round( val = lv_share dec = 2 ). + IF rv_share < c_min_share. + rv_share = c_min_share. + ENDIF. + ENDMETHOD. + + METHOD distance_fee. + IF iv_distance_km > 0. + rv_fee = c_fee_per_km * iv_distance_km. + ENDIF. + ENDMETHOD. + + METHOD detour_fee. + rv_fee = c_detour_fee * ( iv_passengers - 1 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/faulty/m3_rideshare_split.clas.abap b/tasks_gen/train/G1023/faulty/m3_rideshare_split.clas.abap new file mode 100644 index 0000000..37459cc --- /dev/null +++ b/tasks_gen/train/G1023/faulty/m3_rideshare_split.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}rideshare_split DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_distance TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate_total + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS calculate_share + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_base_fee TYPE ty_amount VALUE '3.00', + c_fee_per_km TYPE ty_amount VALUE '0.40', + c_detour_fee TYPE ty_amount VALUE '1.20', + c_min_share TYPE ty_amount VALUE '1.50'. + + METHODS distance_fee + IMPORTING iv_distance_km TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS detour_fee + IMPORTING iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}rideshare_split IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_total. + IF iv_passengers < 1. + RETURN. + ENDIF. + rv_total = c_base_fee + distance_fee( iv_distance_km ) + detour_fee( iv_passengers ). + ENDMETHOD. + + METHOD calculate_share. + IF iv_passengers < 1. + RETURN. + ENDIF. + DATA(lv_total) = calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ). + DATA lv_share TYPE p LENGTH 15 DECIMALS 5. + lv_share = lv_total / iv_passengers. + rv_share = round( val = lv_share dec = 2 ). + IF rv_share < c_min_share. + rv_share = c_min_share. + ENDIF. + ENDMETHOD. + + METHOD distance_fee. + IF iv_distance_km <= 0. + rv_fee = c_fee_per_km * iv_distance_km. + ENDIF. + ENDMETHOD. + + METHOD detour_fee. + rv_fee = c_detour_fee * ( iv_passengers - 1 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/faulty/m4_rideshare_split.clas.abap b/tasks_gen/train/G1023/faulty/m4_rideshare_split.clas.abap new file mode 100644 index 0000000..3e78ddd --- /dev/null +++ b/tasks_gen/train/G1023/faulty/m4_rideshare_split.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}rideshare_split DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_distance TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate_total + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS calculate_share + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_base_fee TYPE ty_amount VALUE '3.00', + c_fee_per_km TYPE ty_amount VALUE '0.40', + c_detour_fee TYPE ty_amount VALUE '1.20', + c_min_share TYPE ty_amount VALUE '1.50'. + + METHODS distance_fee + IMPORTING iv_distance_km TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS detour_fee + IMPORTING iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}rideshare_split IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_total. + IF iv_passengers < 1. + RETURN. + ENDIF. + rv_total = c_base_fee + distance_fee( iv_distance_km ) + detour_fee( iv_passengers ). + ENDMETHOD. + + METHOD calculate_share. + IF iv_passengers < 1. + RETURN. + ENDIF. + DATA(lv_total) = calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ). + DATA lv_share TYPE p LENGTH 15 DECIMALS 5. + lv_share = lv_total / iv_passengers. + rv_share = round( val = lv_share dec = 2 ). + IF rv_share < c_min_share. + rv_share = c_min_share. + ENDIF. + ENDMETHOD. + + METHOD distance_fee. + IF iv_distance_km > 0. + rv_fee = c_fee_per_km / iv_distance_km. + ENDIF. + ENDMETHOD. + + METHOD detour_fee. + rv_fee = c_detour_fee * ( iv_passengers - 1 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/generation.json b/tasks_gen/train/G1023/generation.json new file mode 100644 index 0000000..2016480 --- /dev/null +++ b/tasks_gen/train/G1023/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1023", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 88.5, + "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/G1023/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1023/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..7e121b6 --- /dev/null +++ b/tasks_gen/train/G1023/hidden/t21_hidden.clas.abap @@ -0,0 +1,93 @@ +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}}rideshare_split. + + METHODS setup. + METHODS assert_split + IMPORTING iv_distance_km TYPE {{p}}rideshare_split=>ty_distance + iv_passengers TYPE i + iv_exp_total TYPE {{p}}rideshare_split=>ty_amount + iv_exp_share TYPE {{p}}rideshare_split=>ty_amount. + + METHODS no_passengers FOR TESTING. + METHODS negative_passengers FOR TESTING. + METHODS single_passenger FOR TESTING. + METHODS distance_and_detour FOR TESTING. + METHODS negative_distance FOR TESTING. + METHODS rounds_half_away FOR TESTING. + METHODS minimum_share FOR TESTING. + METHODS minimum_boundary FOR TESTING. + METHODS long_ride FOR TESTING. + METHODS two_passengers FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}rideshare_split( ). + ENDMETHOD. + + METHOD assert_split. + cl_abap_unit_assert=>assert_equals( + exp = iv_exp_total + act = mo_cut->calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ) + msg = |total, { iv_distance_km } km, { iv_passengers } passengers| ). + cl_abap_unit_assert=>assert_equals( + exp = iv_exp_share + act = mo_cut->calculate_share( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ) + msg = |share, { iv_distance_km } km, { iv_passengers } passengers| ). + ENDMETHOD. + + METHOD no_passengers. + assert_split( iv_distance_km = 10 iv_passengers = 0 + iv_exp_total = '0.00' iv_exp_share = '0.00' ). + ENDMETHOD. + + METHOD negative_passengers. + assert_split( iv_distance_km = 5 iv_passengers = -3 + iv_exp_total = '0.00' iv_exp_share = '0.00' ). + ENDMETHOD. + + METHOD single_passenger. + assert_split( iv_distance_km = 0 iv_passengers = 1 + iv_exp_total = '3.00' iv_exp_share = '3.00' ). + ENDMETHOD. + + METHOD distance_and_detour. + assert_split( iv_distance_km = 10 iv_passengers = 3 + iv_exp_total = '9.40' iv_exp_share = '3.13' ). + ENDMETHOD. + + METHOD negative_distance. + assert_split( iv_distance_km = -5 iv_passengers = 2 + iv_exp_total = '4.20' iv_exp_share = '2.10' ). + ENDMETHOD. + + METHOD rounds_half_away. + assert_split( iv_distance_km = 2 iv_passengers = 8 + iv_exp_total = '12.20' iv_exp_share = '1.53' ). + ENDMETHOD. + + METHOD minimum_share. + assert_split( iv_distance_km = 0 iv_passengers = 7 + iv_exp_total = '10.20' iv_exp_share = '1.50' ). + ENDMETHOD. + + METHOD minimum_boundary. + assert_split( iv_distance_km = 0 iv_passengers = 6 + iv_exp_total = '9.00' iv_exp_share = '1.50' ). + ENDMETHOD. + + METHOD long_ride. + assert_split( iv_distance_km = 100 iv_passengers = 5 + iv_exp_total = '47.80' iv_exp_share = '9.56' ). + ENDMETHOD. + + METHOD two_passengers. + assert_split( iv_distance_km = 5 iv_passengers = 2 + iv_exp_total = '6.20' iv_exp_share = '3.10' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/mutation.json b/tasks_gen/train/G1023/mutation.json new file mode 100644 index 0000000..2a7676d --- /dev/null +++ b/tasks_gen/train/G1023/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1023", + "mutants": [ + { + "object": "{{P}}RIDESHARE_SPLIT", + "mutant": "line 40: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "DISTANCE_AND_DETOUR", + "LONG_RIDE", + "ROUNDS_HALF_AWAY", + "TWO_PASSENGERS" + ] + }, + { + "object": "{{P}}RIDESHARE_SPLIT", + "mutant": "line 44: < -> >= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "DISTANCE_AND_DETOUR", + "LONG_RIDE", + "MINIMUM_BOUNDARY", + "MINIMUM_SHARE", + "NEGATIVE_DISTANCE", + "NEGATIVE_PASSENGERS", + "NO_PASSENGERS", + "ROUNDS_HALF_AWAY", + "SINGLE_PASSENGER", + "TWO_PASSENGERS" + ] + }, + { + "object": "{{P}}RIDESHARE_SPLIT", + "mutant": "line 51: 2 -> 3 (const)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + }, + { + "object": "{{P}}RIDESHARE_SPLIT", + "mutant": "line 58: > -> <= (rel)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "DISTANCE_AND_DETOUR", + "LONG_RIDE", + "NEGATIVE_DISTANCE", + "ROUNDS_HALF_AWAY", + "TWO_PASSENGERS" + ] + }, + { + "object": "{{P}}RIDESHARE_SPLIT", + "mutant": "line 59: * -> / (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "DISTANCE_AND_DETOUR", + "LONG_RIDE", + "ROUNDS_HALF_AWAY", + "TWO_PASSENGERS" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1023/reference/rideshare_split.clas.abap b/tasks_gen/train/G1023/reference/rideshare_split.clas.abap new file mode 100644 index 0000000..91cf4bc --- /dev/null +++ b/tasks_gen/train/G1023/reference/rideshare_split.clas.abap @@ -0,0 +1,66 @@ +CLASS {{p}}rideshare_split DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_distance TYPE i. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + METHODS constructor. + METHODS calculate_total + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS calculate_share + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_base_fee TYPE ty_amount VALUE '3.00', + c_fee_per_km TYPE ty_amount VALUE '0.40', + c_detour_fee TYPE ty_amount VALUE '1.20', + c_min_share TYPE ty_amount VALUE '1.50'. + + METHODS distance_fee + IMPORTING iv_distance_km TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS detour_fee + IMPORTING iv_passengers TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}rideshare_split IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD calculate_total. + IF iv_passengers < 1. + RETURN. + ENDIF. + rv_total = c_base_fee + distance_fee( iv_distance_km ) + detour_fee( iv_passengers ). + ENDMETHOD. + + METHOD calculate_share. + IF iv_passengers < 1. + RETURN. + ENDIF. + DATA(lv_total) = calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ). + DATA lv_share TYPE p LENGTH 15 DECIMALS 5. + lv_share = lv_total / iv_passengers. + rv_share = round( val = lv_share dec = 2 ). + IF rv_share < c_min_share. + rv_share = c_min_share. + ENDIF. + ENDMETHOD. + + METHOD distance_fee. + IF iv_distance_km > 0. + rv_fee = c_fee_per_km * iv_distance_km. + ENDIF. + ENDMETHOD. + + METHOD detour_fee. + rv_fee = c_detour_fee * ( iv_passengers - 1 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/reference/rideshare_split.testclasses.abap b/tasks_gen/train/G1023/reference/rideshare_split.testclasses.abap new file mode 100644 index 0000000..e32c3fe --- /dev/null +++ b/tasks_gen/train/G1023/reference/rideshare_split.testclasses.abap @@ -0,0 +1,66 @@ +CLASS ltc_rideshare_split DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}rideshare_split. + + METHODS setup. + METHODS assert_split + IMPORTING iv_distance_km TYPE {{p}}rideshare_split=>ty_distance + iv_passengers TYPE i + iv_exp_total TYPE {{p}}rideshare_split=>ty_amount + iv_exp_share TYPE {{p}}rideshare_split=>ty_amount. + + METHODS no_passengers FOR TESTING. + METHODS single_passenger FOR TESTING. + METHODS distance_and_detour FOR TESTING. + METHODS negative_distance FOR TESTING. + METHODS rounds_half_away FOR TESTING. + METHODS minimum_share FOR TESTING. +ENDCLASS. + + +CLASS ltc_rideshare_split IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}rideshare_split( ). + ENDMETHOD. + + METHOD assert_split. + cl_abap_unit_assert=>assert_equals( + exp = iv_exp_total + act = mo_cut->calculate_total( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ) ). + cl_abap_unit_assert=>assert_equals( + exp = iv_exp_share + act = mo_cut->calculate_share( iv_distance_km = iv_distance_km + iv_passengers = iv_passengers ) ). + ENDMETHOD. + + METHOD no_passengers. + assert_split( iv_distance_km = 12 iv_passengers = 0 + iv_exp_total = '0.00' iv_exp_share = '0.00' ). + ENDMETHOD. + + METHOD single_passenger. + assert_split( iv_distance_km = 4 iv_passengers = 1 + iv_exp_total = '4.60' iv_exp_share = '4.60' ). + ENDMETHOD. + + METHOD distance_and_detour. + assert_split( iv_distance_km = 10 iv_passengers = 3 + iv_exp_total = '9.40' iv_exp_share = '3.13' ). + ENDMETHOD. + + METHOD negative_distance. + assert_split( iv_distance_km = -2 iv_passengers = 2 + iv_exp_total = '4.20' iv_exp_share = '2.10' ). + ENDMETHOD. + + METHOD rounds_half_away. + assert_split( iv_distance_km = 2 iv_passengers = 8 + iv_exp_total = '12.20' iv_exp_share = '1.53' ). + ENDMETHOD. + + METHOD minimum_share. + assert_split( iv_distance_km = 0 iv_passengers = 7 + iv_exp_total = '10.20' iv_exp_share = '1.50' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1023/spec.md b/tasks_gen/train/G1023/spec.md new file mode 100644 index 0000000..24c6b62 --- /dev/null +++ b/tasks_gen/train/G1023/spec.md @@ -0,0 +1,59 @@ +# 1. Goal +A ride-sharing service splits the cost of a shared ride between the passengers. +The booking program calls the calculation after each completed ride. The program +needs the total cost of the ride and the share of each passenger. + +# 2. Open questions +None. + +# 3. Context +- A ride has one driver and one or more passengers. The driver is not a + passenger and pays nothing. +- The service charges a base fee, a distance fee and a detour fee. +- The booking program knows the distance of the ride in whole kilometres and + the number of passengers of the ride. + +# 4. Contract +- Create the class {{P}}RIDESHARE_SPLIT in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - TY_DISTANCE: a whole number of kilometres. + - TY_AMOUNT: a packed number with 2 decimals. The currency is euro. +- The class has these public methods. Do not add other public methods. + - CALCULATE_TOTAL + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount + - CALCULATE_SHARE + IMPORTING iv_distance_km TYPE ty_distance + iv_passengers TYPE i + RETURNING VALUE(rv_share) TYPE ty_amount + +# 5. Business rules +Apply the rules in this sequence. +1. The number of passengers is the number of persons who share the ride. +2. If the number of passengers is less than 1, the total cost is 0.00 and the + share is 0.00. Rules 3 to 7 do not apply. +3. A distance below 0 counts as 0 kilometres. +4. The total cost of the ride is the sum of: + - a base fee of 3.00, + - a distance fee of 0.40 for each kilometre, + - a detour fee of 1.20 for each passenger after the first passenger. +5. The share of a passenger is the total cost divided by the number of + passengers. +6. Round the share to 2 decimals. Round a half away from zero (for example, + 2.345 becomes 2.35). +7. The minimum share is 1.50. If the rounded share is below 1.50, the share is + 1.50. The minimum share does not change the total cost. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Methods 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, + no interface. + +# 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/G1023/task.json b/tasks_gen/train/G1023/task.json new file mode 100644 index 0000000..b073b71 --- /dev/null +++ b/tasks_gen/train/G1023/task.json @@ -0,0 +1,47 @@ +{ + "id": "G1023", + "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}}RIDESHARE_SPLIT" + } + ], + "out_of_scope": [ + "database tables", + "CDS views", + "reports", + "function modules", + "interfaces" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests for the ride-sharing cost split" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}RIDESHARE_SPLIT", + "file": "reference/rideshare_split.clas.abap", + "description": "Ride-sharing cost split calculator", + "testclasses_file": "reference/rideshare_split.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1024/faulty/m0_holiday_checker.clas.abap b/tasks_gen/train/G1024/faulty/m0_holiday_checker.clas.abap new file mode 100644 index 0000000..7d4de35 --- /dev/null +++ b/tasks_gen/train/G1024/faulty/m0_holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers >= c_min_travellers + OR is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights < c_min_nights OR is_booking-nights > c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today + c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/faulty/m1_holiday_checker.clas.abap b/tasks_gen/train/G1024/faulty/m1_holiday_checker.clas.abap new file mode 100644 index 0000000..bef8221 --- /dev/null +++ b/tasks_gen/train/G1024/faulty/m1_holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers < c_min_travellers + AND is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights < c_min_nights OR is_booking-nights > c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today + c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/faulty/m2_holiday_checker.clas.abap b/tasks_gen/train/G1024/faulty/m2_holiday_checker.clas.abap new file mode 100644 index 0000000..b72893a --- /dev/null +++ b/tasks_gen/train/G1024/faulty/m2_holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers < c_min_travellers + OR is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights >= c_min_nights OR is_booking-nights > c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today + c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/faulty/m3_holiday_checker.clas.abap b/tasks_gen/train/G1024/faulty/m3_holiday_checker.clas.abap new file mode 100644 index 0000000..cca2eb6 --- /dev/null +++ b/tasks_gen/train/G1024/faulty/m3_holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers < c_min_travellers + OR is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights < c_min_nights OR is_booking-nights <= c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today + c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/faulty/m4_holiday_checker.clas.abap b/tasks_gen/train/G1024/faulty/m4_holiday_checker.clas.abap new file mode 100644 index 0000000..b0219e1 --- /dev/null +++ b/tasks_gen/train/G1024/faulty/m4_holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers < c_min_travellers + OR is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights < c_min_nights OR is_booking-nights > c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today - c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/generation.json b/tasks_gen/train/G1024/generation.json new file mode 100644 index 0000000..e41259a --- /dev/null +++ b/tasks_gen/train/G1024/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1024", + "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/G1024/hidden/holiday_hidden.clas.abap b/tasks_gen/train/G1024/hidden/holiday_hidden.clas.abap new file mode 100644 index 0000000..24b1887 --- /dev/null +++ b/tasks_gen/train/G1024/hidden/holiday_hidden.clas.abap @@ -0,0 +1,184 @@ +CLASS {{p}}holiday_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CONSTANTS c_today TYPE d VALUE '20260101'. + + DATA mo_cut TYPE REF TO {{p}}holiday_checker. + + METHODS setup. + METHODS valid_booking FOR TESTING. + METHODS minimum_values FOR TESTING. + METHODS lower_case_code FOR TESTING. + METHODS unknown_destination FOR TESTING. + METHODS too_many_travellers FOR TESTING. + METHODS zero_travellers FOR TESTING. + METHODS bad_nights FOR TESTING. + METHODS too_early_departure FOR TESTING. + METHODS dest_rule_comes_first FOR TESTING. + METHODS nights_rule_comes_first FOR TESTING. +ENDCLASS. + + +CLASS {{p}}holiday_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}holiday_checker( ). + ENDMETHOD. + + METHOD valid_booking. + DATA(lv_done) = abap_false. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'ALPS' + travellers = 6 + nights = 7 + departure = '20260201' ) + iv_today = c_today ). + lv_done = abap_true. + CATCH {{p}}cx_holiday_error INTO DATA(lx). + cl_abap_unit_assert=>fail( |Unexpected exception: { lx->mv_message }| ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_done ). + ENDMETHOD. + + METHOD minimum_values. + DATA(lv_done) = abap_false. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'LAPLAND' + travellers = 1 + nights = 2 + departure = '20260115' ) + iv_today = c_today ). + lv_done = abap_true. + CATCH {{p}}cx_holiday_error INTO DATA(lx). + cl_abap_unit_assert=>fail( |Unexpected exception: { lx->mv_message }| ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_done ). + ENDMETHOD. + + METHOD lower_case_code. + DATA(lv_done) = abap_false. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = ' kyoto ' + travellers = 8 + nights = 14 + departure = '20260301' ) + iv_today = c_today ). + lv_done = abap_true. + CATCH {{p}}cx_holiday_error INTO DATA(lx). + cl_abap_unit_assert=>fail( |Unexpected exception: { lx->mv_message }| ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_done ). + ENDMETHOD. + + METHOD unknown_destination. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'MARS' + travellers = 2 + nights = 7 + departure = '20260201' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_UNKNOWN_DEST expected' ). + CATCH {{p}}cx_unknown_dest INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = `MARS` act = lx->mv_dest ). + cl_abap_unit_assert=>assert_equals( exp = `Destination is not offered` + act = lx->mv_message ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD too_many_travellers. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'CRETE' + travellers = 5 + nights = 7 + departure = '20260201' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_TRAVELLERS expected' ). + CATCH {{p}}cx_bad_travellers INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lx->mv_travellers ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lx->mv_max ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD zero_travellers. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'ALPS' + travellers = 0 + nights = 7 + departure = '20260201' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_TRAVELLERS expected' ). + CATCH {{p}}cx_bad_travellers INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lx->mv_travellers ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lx->mv_max ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD bad_nights. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'ALPS' + travellers = 2 + nights = 29 + departure = '20260201' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_NIGHTS expected' ). + CATCH {{p}}cx_bad_nights INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = 29 act = lx->mv_nights ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD too_early_departure. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'SAFARI' + travellers = 3 + nights = 7 + departure = '20260114' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_DEPARTURE expected' ). + CATCH {{p}}cx_bad_departure INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = CONV d( '20260114' ) + act = lx->mv_departure ). + cl_abap_unit_assert=>assert_equals( exp = CONV d( '20260115' ) + act = lx->mv_earliest ). + cl_abap_unit_assert=>assert_equals( exp = `Departure date is too early` + act = lx->mv_message ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD dest_rule_comes_first. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'MARS' + travellers = 99 + nights = 99 + departure = '20200101' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_UNKNOWN_DEST expected' ). + CATCH {{p}}cx_unknown_dest. + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD nights_rule_comes_first. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'ALPS' + travellers = 2 + nights = 99 + departure = '20200101' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_NIGHTS expected' ). + CATCH {{p}}cx_bad_nights. + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/mutation.json b/tasks_gen/train/G1024/mutation.json new file mode 100644 index 0000000..6f0566a --- /dev/null +++ b/tasks_gen/train/G1024/mutation.json @@ -0,0 +1,69 @@ +{ + "task": "G1024", + "mutants": [ + { + "object": "{{P}}HOLIDAY_CHECKER", + "mutant": "line 85: < -> >= (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "BAD_NIGHTS", + "LOWER_CASE_CODE", + "MINIMUM_VALUES", + "NIGHTS_RULE_COMES_FIRST", + "TOO_EARLY_DEPARTURE", + "VALID_BOOKING", + "ZERO_TRAVELLERS" + ] + }, + { + "object": "{{P}}HOLIDAY_CHECKER", + "mutant": "line 86: OR -> AND (logic)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "TOO_MANY_TRAVELLERS", + "ZERO_TRAVELLERS" + ] + }, + { + "object": "{{P}}HOLIDAY_CHECKER", + "mutant": "line 94: < -> >= (rel)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "LOWER_CASE_CODE", + "MINIMUM_VALUES", + "TOO_EARLY_DEPARTURE", + "VALID_BOOKING" + ] + }, + { + "object": "{{P}}HOLIDAY_CHECKER", + "mutant": "line 94: > -> <= (rel)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "BAD_NIGHTS", + "LOWER_CASE_CODE", + "MINIMUM_VALUES", + "NIGHTS_RULE_COMES_FIRST", + "TOO_EARLY_DEPARTURE", + "VALID_BOOKING" + ] + }, + { + "object": "{{P}}HOLIDAY_CHECKER", + "mutant": "line 102: + -> - (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "TOO_EARLY_DEPARTURE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1024/reference/cx_bad_departure.clas.abap b/tasks_gen/train/G1024/reference/cx_bad_departure.clas.abap new file mode 100644 index 0000000..8e10559 --- /dev/null +++ b/tasks_gen/train/G1024/reference/cx_bad_departure.clas.abap @@ -0,0 +1,25 @@ +CLASS {{p}}cx_bad_departure DEFINITION + PUBLIC + FINAL + INHERITING FROM {{p}}cx_holiday_error + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_departure TYPE d READ-ONLY. + DATA mv_earliest TYPE d READ-ONLY. + + METHODS constructor + IMPORTING iv_departure TYPE d + iv_earliest TYPE d. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_bad_departure IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_message = 'Departure date is too early' ). + mv_departure = iv_departure. + mv_earliest = iv_earliest. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/reference/cx_bad_nights.clas.abap b/tasks_gen/train/G1024/reference/cx_bad_nights.clas.abap new file mode 100644 index 0000000..de38a04 --- /dev/null +++ b/tasks_gen/train/G1024/reference/cx_bad_nights.clas.abap @@ -0,0 +1,22 @@ +CLASS {{p}}cx_bad_nights DEFINITION + PUBLIC + FINAL + INHERITING FROM {{p}}cx_holiday_error + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_nights TYPE i READ-ONLY. + + METHODS constructor + IMPORTING iv_nights TYPE i. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_bad_nights IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_message = 'Number of nights is not allowed' ). + mv_nights = iv_nights. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/reference/cx_bad_travellers.clas.abap b/tasks_gen/train/G1024/reference/cx_bad_travellers.clas.abap new file mode 100644 index 0000000..c1afe17 --- /dev/null +++ b/tasks_gen/train/G1024/reference/cx_bad_travellers.clas.abap @@ -0,0 +1,25 @@ +CLASS {{p}}cx_bad_travellers DEFINITION + PUBLIC + FINAL + INHERITING FROM {{p}}cx_holiday_error + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_travellers TYPE i READ-ONLY. + DATA mv_max TYPE i READ-ONLY. + + METHODS constructor + IMPORTING iv_travellers TYPE i + iv_max TYPE i. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_bad_travellers IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_message = 'Number of travellers is not allowed' ). + mv_travellers = iv_travellers. + mv_max = iv_max. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/reference/cx_unknown_dest.clas.abap b/tasks_gen/train/G1024/reference/cx_unknown_dest.clas.abap new file mode 100644 index 0000000..8b86210 --- /dev/null +++ b/tasks_gen/train/G1024/reference/cx_unknown_dest.clas.abap @@ -0,0 +1,22 @@ +CLASS {{p}}cx_unknown_dest DEFINITION + PUBLIC + FINAL + INHERITING FROM {{p}}cx_holiday_error + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_dest TYPE string READ-ONLY. + + METHODS constructor + IMPORTING iv_dest TYPE string. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_unknown_dest IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_message = 'Destination is not offered' ). + mv_dest = iv_dest. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/reference/holiday_checker.clas.abap b/tasks_gen/train/G1024/reference/holiday_checker.clas.abap new file mode 100644 index 0000000..a7b243d --- /dev/null +++ b/tasks_gen/train/G1024/reference/holiday_checker.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}holiday_checker DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_dest_code TYPE string. + + TYPES: + BEGIN OF ty_booking, + dest_code TYPE ty_dest_code, + travellers TYPE i, + nights TYPE i, + departure TYPE d, + END OF ty_booking. + + METHODS check + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. + + PRIVATE SECTION. + CONSTANTS: + c_min_travellers TYPE i VALUE 1, + c_min_nights TYPE i VALUE 2, + c_max_nights TYPE i VALUE 28, + c_lead_days TYPE i VALUE 14. + + METHODS normalize + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_code) TYPE ty_dest_code. + + METHODS max_travellers + IMPORTING iv_code TYPE ty_dest_code + RETURNING VALUE(rv_max) TYPE i + RAISING {{p}}cx_unknown_dest. + + METHODS check_travellers + IMPORTING is_booking TYPE ty_booking + iv_max TYPE i + RAISING {{p}}cx_holiday_error. + + METHODS check_nights + IMPORTING is_booking TYPE ty_booking + RAISING {{p}}cx_holiday_error. + + METHODS check_departure + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{p}}cx_holiday_error. +ENDCLASS. + + +CLASS {{p}}holiday_checker IMPLEMENTATION. + METHOD check. + DATA(lv_code) = normalize( is_booking-dest_code ). + DATA(lv_max) = max_travellers( lv_code ). + check_travellers( is_booking = is_booking iv_max = lv_max ). + check_nights( is_booking ). + check_departure( is_booking = is_booking iv_today = iv_today ). + ENDMETHOD. + + METHOD normalize. + rv_code = to_upper( replace( val = iv_code sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD max_travellers. + CASE iv_code. + WHEN 'ALPS'. + rv_max = 6. + WHEN 'CRETE'. + rv_max = 4. + WHEN 'KYOTO'. + rv_max = 8. + WHEN 'LAPLAND'. + rv_max = 5. + WHEN 'SAFARI'. + rv_max = 7. + WHEN OTHERS. + RAISE EXCEPTION TYPE {{p}}cx_unknown_dest EXPORTING iv_dest = iv_code. + ENDCASE. + ENDMETHOD. + + METHOD check_travellers. + IF is_booking-travellers < c_min_travellers + OR is_booking-travellers > iv_max. + RAISE EXCEPTION TYPE {{p}}cx_bad_travellers + EXPORTING iv_travellers = is_booking-travellers + iv_max = iv_max. + ENDIF. + ENDMETHOD. + + METHOD check_nights. + IF is_booking-nights < c_min_nights OR is_booking-nights > c_max_nights. + RAISE EXCEPTION TYPE {{p}}cx_bad_nights + EXPORTING iv_nights = is_booking-nights. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + DATA lv_earliest TYPE d. + lv_earliest = iv_today + c_lead_days. + IF is_booking-departure < lv_earliest. + RAISE EXCEPTION TYPE {{p}}cx_bad_departure + EXPORTING iv_departure = is_booking-departure + iv_earliest = lv_earliest. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/reference/holiday_checker.testclasses.abap b/tasks_gen/train/G1024/reference/holiday_checker.testclasses.abap new file mode 100644 index 0000000..0214009 --- /dev/null +++ b/tasks_gen/train/G1024/reference/holiday_checker.testclasses.abap @@ -0,0 +1,115 @@ +CLASS ltc_holiday_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CONSTANTS c_today TYPE d VALUE '20260101'. + + DATA mo_cut TYPE REF TO {{p}}holiday_checker. + + METHODS setup. + METHODS valid_booking FOR TESTING. + METHODS minimum_values FOR TESTING. + METHODS unknown_destination FOR TESTING. + METHODS too_many_travellers FOR TESTING. + METHODS bad_nights FOR TESTING. + METHODS early_departure FOR TESTING. +ENDCLASS. + + +CLASS ltc_holiday_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}holiday_checker( ). + ENDMETHOD. + + METHOD valid_booking. + DATA(lv_done) = abap_false. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'KYOTO' + travellers = 8 + nights = 10 + departure = '20260301' ) + iv_today = c_today ). + lv_done = abap_true. + CATCH {{p}}cx_holiday_error INTO DATA(lx). + cl_abap_unit_assert=>fail( |Unexpected exception: { lx->mv_message }| ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_done ). + ENDMETHOD. + + METHOD minimum_values. + DATA(lv_done) = abap_false. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'LAPLAND' + travellers = 1 + nights = 2 + departure = '20260115' ) + iv_today = c_today ). + lv_done = abap_true. + CATCH {{p}}cx_holiday_error INTO DATA(lx). + cl_abap_unit_assert=>fail( |Unexpected exception: { lx->mv_message }| ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_done ). + ENDMETHOD. + + METHOD unknown_destination. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'MARS' + travellers = 2 + nights = 7 + departure = '20260301' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_UNKNOWN_DEST expected' ). + CATCH {{p}}cx_unknown_dest INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = `MARS` act = lx->mv_dest ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD too_many_travellers. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'SAFARI' + travellers = 8 + nights = 7 + departure = '20260301' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_TRAVELLERS expected' ). + CATCH {{p}}cx_bad_travellers INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = 8 act = lx->mv_travellers ). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lx->mv_max ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD bad_nights. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'ALPS' + travellers = 2 + nights = 30 + departure = '20260301' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_NIGHTS expected' ). + CATCH {{p}}cx_bad_nights INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = 30 act = lx->mv_nights ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD early_departure. + TRY. + mo_cut->check( is_booking = VALUE #( dest_code = 'CRETE' + travellers = 2 + nights = 7 + departure = '20260110' ) + iv_today = c_today ). + cl_abap_unit_assert=>fail( 'CX_BAD_DEPARTURE expected' ). + CATCH {{p}}cx_bad_departure INTO DATA(lx). + cl_abap_unit_assert=>assert_equals( exp = CONV d( '20260115' ) + act = lx->mv_earliest ). + CATCH {{p}}cx_holiday_error. + cl_abap_unit_assert=>fail( 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/seed/cx_holiday_error.clas.abap b/tasks_gen/train/G1024/seed/cx_holiday_error.clas.abap new file mode 100644 index 0000000..dff6861 --- /dev/null +++ b/tasks_gen/train/G1024/seed/cx_holiday_error.clas.abap @@ -0,0 +1,22 @@ +CLASS {{p}}cx_holiday_error DEFINITION + PUBLIC + ABSTRACT + INHERITING FROM cx_static_check + CREATE PUBLIC. + + PUBLIC SECTION. + DATA mv_message TYPE string READ-ONLY. + + METHODS constructor + IMPORTING iv_message TYPE string. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_holiday_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_message = iv_message. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1024/spec.md b/tasks_gen/train/G1024/spec.md new file mode 100644 index 0000000..47f8c06 --- /dev/null +++ b/tasks_gen/train/G1024/spec.md @@ -0,0 +1,97 @@ +# 1. Goal +A tour operator checks a holiday booking before it confirms the booking. The +booking desk calls one check method for each booking. The check method reports +each problem as a class-based exception. Each exception carries a message and +the values that caused the problem. + +# 2. Open questions +None. + +# 3. Context +- Class {{P}}CX_HOLIDAY_ERROR exists in package $TMP. It is active. It is + abstract and inherits from cx_static_check. It has the public read-only + attribute mv_message TYPE string and a public constructor with the importing + parameter iv_message TYPE string. The constructor sets mv_message. +- The tour operator offers five destinations. The destination code and the + maximum number of travellers of each destination are: + ALPS 6, CRETE 4, KYOTO 8, LAPLAND 5, SAFARI 7. +- The booking desk passes the booking and the current date to the check method. +- A booking has a destination code, a number of travellers, a number of nights, + and a departure date. + +# 4. Contract +Create these five classes in package $TMP. Do not change {{P}}CX_HOLIDAY_ERROR. + +1. Class {{P}}HOLIDAY_CHECKER + - The class is public and final. It has a public constructor without + parameters. + - Public type ty_dest_code, type string. + - Public type ty_booking, a structure with these components: + dest_code TYPE ty_dest_code, travellers TYPE i, nights TYPE i, + departure TYPE d. + - Public method check with this signature: + IMPORTING is_booking TYPE ty_booking + iv_today TYPE d + RAISING {{P}}CX_HOLIDAY_ERROR + - Do not add other public methods. + +2. Class {{P}}CX_UNKNOWN_DEST + - The class is final and inherits from {{P}}CX_HOLIDAY_ERROR. + - Public read-only attribute mv_dest TYPE string. + - Public constructor with the importing parameter iv_dest TYPE string. + - mv_message is 'Destination is not offered'. + - mv_dest holds the normalized destination code. The value has no leading or + trailing blanks. + +3. Class {{P}}CX_BAD_TRAVELLERS + - The class is final and inherits from {{P}}CX_HOLIDAY_ERROR. + - Public read-only attributes mv_travellers TYPE i and mv_max TYPE i. + - Public constructor with the importing parameters iv_travellers TYPE i and + iv_max TYPE i. + - mv_message is 'Number of travellers is not allowed'. + +4. Class {{P}}CX_BAD_NIGHTS + - The class is final and inherits from {{P}}CX_HOLIDAY_ERROR. + - Public read-only attribute mv_nights TYPE i. + - Public constructor with the importing parameter iv_nights TYPE i. + - mv_message is 'Number of nights is not allowed'. + +5. Class {{P}}CX_BAD_DEPARTURE + - The class is final and inherits from {{P}}CX_HOLIDAY_ERROR. + - Public read-only attributes mv_departure TYPE d and mv_earliest TYPE d. + - Public constructor with the importing parameters iv_departure TYPE d and + iv_earliest TYPE d. + - mv_message is 'Departure date is too early'. + +# 5. Business rules +The method check applies these rules in this sequence. The method raises the +exception of the first rule that fails. If no rule fails, the method returns +without an exception. + +1. Normalize the destination code: remove all spaces and convert all letters to + upper case. +2. The normalized code must be ALPS, CRETE, KYOTO, LAPLAND, or SAFARI. If it is + not, raise {{P}}CX_UNKNOWN_DEST. Pass the normalized code in iv_dest. +3. The number of travellers must be 1 or more. It must not be more than the + maximum number of travellers of the destination. If a value is not correct, + raise {{P}}CX_BAD_TRAVELLERS. Pass the number of travellers in iv_travellers + and the maximum of the destination in iv_max. +4. The number of nights must be 2 or more. It must not be more than 28. If a + value is not correct, raise {{P}}CX_BAD_NIGHTS. Pass the number of nights in + iv_nights. +5. The departure date must be at least 14 days after iv_today. If it is not, + raise {{P}}CX_BAD_DEPARTURE. Pass the departure date in iv_departure and the + earliest possible date (iv_today plus 14 days) in iv_earliest. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}CX_HOLIDAY_ERROR. Do not create a database + table, a CDS view entity, a function module, or a report. Do not change the + signature of check. + +# 7. Acceptance +- All five classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}HOLIDAY_CHECKER. diff --git a/tasks_gen/train/G1024/task.json b/tasks_gen/train/G1024/task.json new file mode 100644 index 0000000..67124a9 --- /dev/null +++ b/tasks_gen/train/G1024/task.json @@ -0,0 +1,94 @@ +{ + "id": "G1024", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}CX_HOLIDAY_ERROR", + "file": "seed/cx_holiday_error.clas.abap", + "description": "Abstract base exception that carries a message" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}HOLIDAY_CHECKER" + }, + { + "type": "CLAS", + "name": "{{P}}CX_UNKNOWN_DEST" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_TRAVELLERS" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_NIGHTS" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_DEPARTURE" + } + ], + "out_of_scope": [ + "{{P}}CX_HOLIDAY_ERROR", + "database tables", + "CDS view entities", + "function modules", + "reports" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}HOLIDAY_HIDDEN", + "file": "hidden/holiday_hidden.clas.abap", + "description": "T07 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_UNKNOWN_DEST", + "file": "reference/cx_unknown_dest.clas.abap", + "description": "Unknown destination exception" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_TRAVELLERS", + "file": "reference/cx_bad_travellers.clas.abap", + "description": "Bad traveller count exception" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_NIGHTS", + "file": "reference/cx_bad_nights.clas.abap", + "description": "Bad night count exception" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAD_DEPARTURE", + "file": "reference/cx_bad_departure.clas.abap", + "description": "Departure date too early exception" + }, + { + "type": "CLAS", + "name": "{{P}}HOLIDAY_CHECKER", + "file": "reference/holiday_checker.clas.abap", + "description": "Holiday booking checker", + "testclasses_file": "reference/holiday_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1025/faulty/m0_crane_hire_calc.clas.abap b/tasks_gen/train/G1025/faulty/m0_crane_hire_calc.clas.abap new file mode 100644 index 0000000..22569a3 --- /dev/null +++ b/tasks_gen/train/G1025/faulty/m0_crane_hire_calc.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}crane_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_crane_hire. + + PRIVATE SECTION. + CONSTANTS: + c_min_radius TYPE i VALUE 5, + c_max_radius TYPE i VALUE 60, + c_min_load TYPE i VALUE 500, + c_permit_fee TYPE i VALUE 250, + c_min_fee TYPE i VALUE 300, + c_radius_rate TYPE i VALUE 15, + c_load_rate TYPE i VALUE 5. + + TYPES: + BEGIN OF ty_band, + radius_max TYPE i, + max_load TYPE i, + END OF ty_band, + tt_band TYPE STANDARD TABLE OF ty_band WITH EMPTY KEY. + + METHODS validate + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + RETURNING VALUE(rv_reason) TYPE {{p}}if_crane_hire=>ty_reason. + METHODS max_load + IMPORTING iv_radius_m TYPE i + RETURNING VALUE(rv_load) TYPE i. + METHODS load_chart + RETURNING VALUE(rt_band) TYPE tt_band. + METHODS ground_factor + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_percent) TYPE i. + METHODS normalize_ground + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_ground) TYPE string. + METHODS apply_percent + IMPORTING iv_value TYPE i + iv_percent TYPE i + RETURNING VALUE(rv_value) TYPE i. +ENDCLASS. + + +CLASS {{p}}crane_hire_calc IMPLEMENTATION. + METHOD {{p}}if_crane_hire~hire. + DATA(lv_reason) = validate( iv_radius_m = iv_radius_m + iv_load_kg = iv_load_kg + iv_ground = iv_ground ). + IF lv_reason <> {{p}}if_crane_hire=>c_reason-ok. + rs_result = VALUE #( reason = lv_reason fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_base) = iv_radius_m * c_radius_rate + ( iv_load_kg DIV 100 ) * c_load_rate. + DATA(lv_fee) = apply_percent( iv_value = lv_base + iv_percent = ground_factor( iv_ground ) ). + IF iv_permit <> abap_true. + lv_fee = lv_fee + c_permit_fee. + ENDIF. + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rs_result = VALUE #( reason = {{p}}if_crane_hire=>c_reason-ok fee = lv_fee ). + ENDMETHOD. + + METHOD validate. + IF iv_radius_m < c_min_radius OR iv_radius_m > c_max_radius. + rv_reason = {{p}}if_crane_hire=>c_reason-radius. + RETURN. + ENDIF. + IF iv_load_kg < c_min_load. + rv_reason = {{p}}if_crane_hire=>c_reason-load. + RETURN. + ENDIF. + IF iv_load_kg > max_load( iv_radius_m ). + rv_reason = {{p}}if_crane_hire=>c_reason-capacity. + RETURN. + ENDIF. + IF ground_factor( iv_ground ) = 0. + rv_reason = {{p}}if_crane_hire=>c_reason-ground. + RETURN. + ENDIF. + rv_reason = {{p}}if_crane_hire=>c_reason-ok. + ENDMETHOD. + + METHOD max_load. + DATA(lt_band) = load_chart( ). + LOOP AT lt_band INTO DATA(ls_band). + IF iv_radius_m <= ls_band-radius_max. + rv_load = ls_band-max_load. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD load_chart. + rt_band = VALUE #( ( radius_max = 10 max_load = 12000 ) + ( radius_max = 20 max_load = 8000 ) + ( radius_max = 30 max_load = 5000 ) + ( radius_max = 45 max_load = 3000 ) + ( radius_max = 60 max_load = 1500 ) ). + ENDMETHOD. + + METHOD ground_factor. + CASE normalize_ground( iv_ground ). + WHEN 'ROAD'. + rv_percent = 100. + WHEN 'GRAVEL'. + rv_percent = 115. + WHEN 'SOIL'. + rv_percent = 135. + ENDCASE. + ENDMETHOD. + + METHOD normalize_ground. + rv_ground = to_upper( condense( iv_ground ) ). + ENDMETHOD. + + METHOD apply_percent. + rv_value = ( iv_value * iv_percent + 50 ) DIV 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/faulty/m1_crane_hire_calc.clas.abap b/tasks_gen/train/G1025/faulty/m1_crane_hire_calc.clas.abap new file mode 100644 index 0000000..66fb1b3 --- /dev/null +++ b/tasks_gen/train/G1025/faulty/m1_crane_hire_calc.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}crane_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_crane_hire. + + PRIVATE SECTION. + CONSTANTS: + c_min_radius TYPE i VALUE 5, + c_max_radius TYPE i VALUE 60, + c_min_load TYPE i VALUE 500, + c_permit_fee TYPE i VALUE 250, + c_min_fee TYPE i VALUE 300, + c_radius_rate TYPE i VALUE 15, + c_load_rate TYPE i VALUE 5. + + TYPES: + BEGIN OF ty_band, + radius_max TYPE i, + max_load TYPE i, + END OF ty_band, + tt_band TYPE STANDARD TABLE OF ty_band WITH EMPTY KEY. + + METHODS validate + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + RETURNING VALUE(rv_reason) TYPE {{p}}if_crane_hire=>ty_reason. + METHODS max_load + IMPORTING iv_radius_m TYPE i + RETURNING VALUE(rv_load) TYPE i. + METHODS load_chart + RETURNING VALUE(rt_band) TYPE tt_band. + METHODS ground_factor + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_percent) TYPE i. + METHODS normalize_ground + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_ground) TYPE string. + METHODS apply_percent + IMPORTING iv_value TYPE i + iv_percent TYPE i + RETURNING VALUE(rv_value) TYPE i. +ENDCLASS. + + +CLASS {{p}}crane_hire_calc IMPLEMENTATION. + METHOD {{p}}if_crane_hire~hire. + DATA(lv_reason) = validate( iv_radius_m = iv_radius_m + iv_load_kg = iv_load_kg + iv_ground = iv_ground ). + IF lv_reason <> {{p}}if_crane_hire=>c_reason-ok. + rs_result = VALUE #( reason = lv_reason fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_base) = iv_radius_m * c_radius_rate + ( iv_load_kg DIV 100 ) * c_load_rate. + DATA(lv_fee) = apply_percent( iv_value = lv_base + iv_percent = ground_factor( iv_ground ) ). + IF iv_permit = abap_true. + lv_fee = lv_fee + c_permit_fee. + ENDIF. + IF lv_fee >= c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rs_result = VALUE #( reason = {{p}}if_crane_hire=>c_reason-ok fee = lv_fee ). + ENDMETHOD. + + METHOD validate. + IF iv_radius_m < c_min_radius OR iv_radius_m > c_max_radius. + rv_reason = {{p}}if_crane_hire=>c_reason-radius. + RETURN. + ENDIF. + IF iv_load_kg < c_min_load. + rv_reason = {{p}}if_crane_hire=>c_reason-load. + RETURN. + ENDIF. + IF iv_load_kg > max_load( iv_radius_m ). + rv_reason = {{p}}if_crane_hire=>c_reason-capacity. + RETURN. + ENDIF. + IF ground_factor( iv_ground ) = 0. + rv_reason = {{p}}if_crane_hire=>c_reason-ground. + RETURN. + ENDIF. + rv_reason = {{p}}if_crane_hire=>c_reason-ok. + ENDMETHOD. + + METHOD max_load. + DATA(lt_band) = load_chart( ). + LOOP AT lt_band INTO DATA(ls_band). + IF iv_radius_m <= ls_band-radius_max. + rv_load = ls_band-max_load. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD load_chart. + rt_band = VALUE #( ( radius_max = 10 max_load = 12000 ) + ( radius_max = 20 max_load = 8000 ) + ( radius_max = 30 max_load = 5000 ) + ( radius_max = 45 max_load = 3000 ) + ( radius_max = 60 max_load = 1500 ) ). + ENDMETHOD. + + METHOD ground_factor. + CASE normalize_ground( iv_ground ). + WHEN 'ROAD'. + rv_percent = 100. + WHEN 'GRAVEL'. + rv_percent = 115. + WHEN 'SOIL'. + rv_percent = 135. + ENDCASE. + ENDMETHOD. + + METHOD normalize_ground. + rv_ground = to_upper( condense( iv_ground ) ). + ENDMETHOD. + + METHOD apply_percent. + rv_value = ( iv_value * iv_percent + 50 ) DIV 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/faulty/m2_crane_hire_calc.clas.abap b/tasks_gen/train/G1025/faulty/m2_crane_hire_calc.clas.abap new file mode 100644 index 0000000..e6c6446 --- /dev/null +++ b/tasks_gen/train/G1025/faulty/m2_crane_hire_calc.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}crane_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_crane_hire. + + PRIVATE SECTION. + CONSTANTS: + c_min_radius TYPE i VALUE 5, + c_max_radius TYPE i VALUE 60, + c_min_load TYPE i VALUE 500, + c_permit_fee TYPE i VALUE 250, + c_min_fee TYPE i VALUE 300, + c_radius_rate TYPE i VALUE 15, + c_load_rate TYPE i VALUE 5. + + TYPES: + BEGIN OF ty_band, + radius_max TYPE i, + max_load TYPE i, + END OF ty_band, + tt_band TYPE STANDARD TABLE OF ty_band WITH EMPTY KEY. + + METHODS validate + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + RETURNING VALUE(rv_reason) TYPE {{p}}if_crane_hire=>ty_reason. + METHODS max_load + IMPORTING iv_radius_m TYPE i + RETURNING VALUE(rv_load) TYPE i. + METHODS load_chart + RETURNING VALUE(rt_band) TYPE tt_band. + METHODS ground_factor + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_percent) TYPE i. + METHODS normalize_ground + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_ground) TYPE string. + METHODS apply_percent + IMPORTING iv_value TYPE i + iv_percent TYPE i + RETURNING VALUE(rv_value) TYPE i. +ENDCLASS. + + +CLASS {{p}}crane_hire_calc IMPLEMENTATION. + METHOD {{p}}if_crane_hire~hire. + DATA(lv_reason) = validate( iv_radius_m = iv_radius_m + iv_load_kg = iv_load_kg + iv_ground = iv_ground ). + IF lv_reason <> {{p}}if_crane_hire=>c_reason-ok. + rs_result = VALUE #( reason = lv_reason fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_base) = iv_radius_m * c_radius_rate + ( iv_load_kg DIV 100 ) * c_load_rate. + DATA(lv_fee) = apply_percent( iv_value = lv_base + iv_percent = ground_factor( iv_ground ) ). + IF iv_permit = abap_true. + lv_fee = lv_fee + c_permit_fee. + ENDIF. + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rs_result = VALUE #( reason = {{p}}if_crane_hire=>c_reason-ok fee = lv_fee ). + ENDMETHOD. + + METHOD validate. + IF iv_radius_m < c_min_radius AND iv_radius_m > c_max_radius. + rv_reason = {{p}}if_crane_hire=>c_reason-radius. + RETURN. + ENDIF. + IF iv_load_kg < c_min_load. + rv_reason = {{p}}if_crane_hire=>c_reason-load. + RETURN. + ENDIF. + IF iv_load_kg > max_load( iv_radius_m ). + rv_reason = {{p}}if_crane_hire=>c_reason-capacity. + RETURN. + ENDIF. + IF ground_factor( iv_ground ) = 0. + rv_reason = {{p}}if_crane_hire=>c_reason-ground. + RETURN. + ENDIF. + rv_reason = {{p}}if_crane_hire=>c_reason-ok. + ENDMETHOD. + + METHOD max_load. + DATA(lt_band) = load_chart( ). + LOOP AT lt_band INTO DATA(ls_band). + IF iv_radius_m <= ls_band-radius_max. + rv_load = ls_band-max_load. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD load_chart. + rt_band = VALUE #( ( radius_max = 10 max_load = 12000 ) + ( radius_max = 20 max_load = 8000 ) + ( radius_max = 30 max_load = 5000 ) + ( radius_max = 45 max_load = 3000 ) + ( radius_max = 60 max_load = 1500 ) ). + ENDMETHOD. + + METHOD ground_factor. + CASE normalize_ground( iv_ground ). + WHEN 'ROAD'. + rv_percent = 100. + WHEN 'GRAVEL'. + rv_percent = 115. + WHEN 'SOIL'. + rv_percent = 135. + ENDCASE. + ENDMETHOD. + + METHOD normalize_ground. + rv_ground = to_upper( condense( iv_ground ) ). + ENDMETHOD. + + METHOD apply_percent. + rv_value = ( iv_value * iv_percent + 50 ) DIV 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/faulty/m4_crane_hire_calc.clas.abap b/tasks_gen/train/G1025/faulty/m4_crane_hire_calc.clas.abap new file mode 100644 index 0000000..e4433f9 --- /dev/null +++ b/tasks_gen/train/G1025/faulty/m4_crane_hire_calc.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}crane_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_crane_hire. + + PRIVATE SECTION. + CONSTANTS: + c_min_radius TYPE i VALUE 5, + c_max_radius TYPE i VALUE 60, + c_min_load TYPE i VALUE 500, + c_permit_fee TYPE i VALUE 250, + c_min_fee TYPE i VALUE 300, + c_radius_rate TYPE i VALUE 15, + c_load_rate TYPE i VALUE 5. + + TYPES: + BEGIN OF ty_band, + radius_max TYPE i, + max_load TYPE i, + END OF ty_band, + tt_band TYPE STANDARD TABLE OF ty_band WITH EMPTY KEY. + + METHODS validate + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + RETURNING VALUE(rv_reason) TYPE {{p}}if_crane_hire=>ty_reason. + METHODS max_load + IMPORTING iv_radius_m TYPE i + RETURNING VALUE(rv_load) TYPE i. + METHODS load_chart + RETURNING VALUE(rt_band) TYPE tt_band. + METHODS ground_factor + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_percent) TYPE i. + METHODS normalize_ground + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_ground) TYPE string. + METHODS apply_percent + IMPORTING iv_value TYPE i + iv_percent TYPE i + RETURNING VALUE(rv_value) TYPE i. +ENDCLASS. + + +CLASS {{p}}crane_hire_calc IMPLEMENTATION. + METHOD {{p}}if_crane_hire~hire. + DATA(lv_reason) = validate( iv_radius_m = iv_radius_m + iv_load_kg = iv_load_kg + iv_ground = iv_ground ). + IF lv_reason <> {{p}}if_crane_hire=>c_reason-ok. + rs_result = VALUE #( reason = lv_reason fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_base) = iv_radius_m * c_radius_rate + ( iv_load_kg DIV 100 ) * c_load_rate. + DATA(lv_fee) = apply_percent( iv_value = lv_base + iv_percent = ground_factor( iv_ground ) ). + IF iv_permit = abap_true. + lv_fee = lv_fee + c_permit_fee. + ENDIF. + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rs_result = VALUE #( reason = {{p}}if_crane_hire=>c_reason-ok fee = lv_fee ). + ENDMETHOD. + + METHOD validate. + IF iv_radius_m < c_min_radius OR iv_radius_m > c_max_radius. + rv_reason = {{p}}if_crane_hire=>c_reason-radius. + RETURN. + ENDIF. + IF iv_load_kg < c_min_load. + rv_reason = {{p}}if_crane_hire=>c_reason-load. + RETURN. + ENDIF. + IF iv_load_kg > max_load( iv_radius_m ). + rv_reason = {{p}}if_crane_hire=>c_reason-capacity. + RETURN. + ENDIF. + IF ground_factor( iv_ground ) = 0. + rv_reason = {{p}}if_crane_hire=>c_reason-ground. + RETURN. + ENDIF. + rv_reason = {{p}}if_crane_hire=>c_reason-ok. + ENDMETHOD. + + METHOD max_load. + DATA(lt_band) = load_chart( ). + LOOP AT lt_band INTO DATA(ls_band). + IF iv_radius_m <= ls_band-radius_max. + rv_load = ls_band-max_load. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD load_chart. + rt_band = VALUE #( ( radius_max = 10 max_load = 12000 ) + ( radius_max = 20 max_load = 8000 ) + ( radius_max = 30 max_load = 5000 ) + ( radius_max = 45 max_load = 3000 ) + ( radius_max = 60 max_load = 1500 ) ). + ENDMETHOD. + + METHOD ground_factor. + CASE normalize_ground( iv_ground ). + WHEN 'ROAD'. + rv_percent = 100. + WHEN 'GRAVEL'. + rv_percent = 115. + WHEN 'SOIL'. + rv_percent = 135. + ENDCASE. + ENDMETHOD. + + METHOD normalize_ground. + rv_ground = to_upper( condense( iv_ground ) ). + ENDMETHOD. + + METHOD apply_percent. + rv_value = ( iv_value / iv_percent + 50 ) DIV 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/generation.json b/tasks_gen/train/G1025/generation.json new file mode 100644 index 0000000..8145108 --- /dev/null +++ b/tasks_gen/train/G1025/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1025", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0150 (similarity spec 0.66, rules 0.67, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/crane_hire_calc.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/crane_hire_calc.clas.abap line 92: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"IF\"" + ], + "by_check": { + "abaplint": 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/G1025/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1025/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..edde119 --- /dev/null +++ b/tasks_gen/train/G1025/hidden/t02_hidden.clas.abap @@ -0,0 +1,234 @@ +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_crane_hire. + METHODS setup. + METHODS base_fee_road FOR TESTING. + METHODS gravel_factor FOR TESTING. + METHODS soil_factor_rounds FOR TESTING. + METHODS permit_surcharge FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS radius_out_of_range FOR TESTING. + METHODS load_out_of_range FOR TESTING. + METHODS capacity_exceeded FOR TESTING. + METHODS capacity_band_edges FOR TESTING. + METHODS unknown_ground FOR TESTING. + METHODS ground_case_spaces FOR TESTING. + METHODS validation_order FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}crane_hire_calc( ). + ENDMETHOD. + + METHOD base_fee_road. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 550 + act = ls_result-fee ). + ENDMETHOD. + + METHOD gravel_factor. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 30 + iv_load_kg = 5000 + iv_ground = `GRAVEL` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 805 + act = ls_result-fee ). + ENDMETHOD. + + METHOD soil_factor_rounds. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 10 + iv_load_kg = 12000 + iv_ground = `SOIL` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 1013 + act = ls_result-fee ). + ENDMETHOD. + + METHOD permit_surcharge. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 800 + act = ls_result-fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 5 + iv_load_kg = 500 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 300 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 5 + iv_load_kg = 500 + iv_ground = `GRAVEL` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 300 + act = ls_result-fee ). + ENDMETHOD. + + METHOD radius_out_of_range. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 4 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-radius + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 61 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-radius + act = ls_result-reason ). + + ls_result = mo_cut->hire( iv_radius_m = 60 + iv_load_kg = 1500 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 975 + act = ls_result-fee ). + ENDMETHOD. + + METHOD load_out_of_range. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 499 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-load + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 5 + iv_load_kg = 500 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD capacity_exceeded. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 25 + iv_load_kg = 6000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 60 + iv_load_kg = 1501 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + ENDMETHOD. + + METHOD capacity_band_edges. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 10 + iv_load_kg = 12000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 750 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 11 + iv_load_kg = 12000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + + ls_result = mo_cut->hire( iv_radius_m = 45 + iv_load_kg = 3000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 825 + act = ls_result-fee ). + + ls_result = mo_cut->hire( iv_radius_m = 46 + iv_load_kg = 3000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + ENDMETHOD. + + METHOD unknown_ground. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ground + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + ENDMETHOD. + + METHOD ground_case_spaces. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 30 + iv_load_kg = 5000 + iv_ground = ` gravel ` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 805 + act = ls_result-fee ). + ENDMETHOD. + + METHOD validation_order. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 4 + iv_load_kg = 100 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-radius + act = ls_result-reason ). + + ls_result = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 100 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-load + act = ls_result-reason ). + + ls_result = mo_cut->hire( iv_radius_m = 25 + iv_load_kg = 6000 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + + ls_result = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ground + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/mutation.json b/tasks_gen/train/G1025/mutation.json new file mode 100644 index 0000000..dbad880 --- /dev/null +++ b/tasks_gen/train/G1025/mutation.json @@ -0,0 +1,73 @@ +{ + "task": "G1025", + "mutants": [ + { + "object": "{{P}}CRANE_HIRE_CALC", + "mutant": "line 58: = -> <> (eq)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "BASE_FEE_ROAD", + "CAPACITY_BAND_EDGES", + "GRAVEL_FACTOR", + "GROUND_CASE_SPACES", + "MINIMUM_FEE", + "PERMIT_SURCHARGE", + "RADIUS_OUT_OF_RANGE", + "SOIL_FACTOR_ROUNDS" + ] + }, + { + "object": "{{P}}CRANE_HIRE_CALC", + "mutant": "line 61: < -> >= (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "BASE_FEE_ROAD", + "CAPACITY_BAND_EDGES", + "GRAVEL_FACTOR", + "GROUND_CASE_SPACES", + "MINIMUM_FEE", + "PERMIT_SURCHARGE", + "RADIUS_OUT_OF_RANGE", + "SOIL_FACTOR_ROUNDS" + ] + }, + { + "object": "{{P}}CRANE_HIRE_CALC", + "mutant": "line 69: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "RADIUS_OUT_OF_RANGE", + "VALIDATION_ORDER" + ] + }, + { + "object": "{{P}}CRANE_HIRE_CALC", + "mutant": "line 102: 3000 -> 3001 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + }, + { + "object": "{{P}}CRANE_HIRE_CALC", + "mutant": "line 122: * -> / (arith)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "BASE_FEE_ROAD", + "CAPACITY_BAND_EDGES", + "GRAVEL_FACTOR", + "GROUND_CASE_SPACES", + "PERMIT_SURCHARGE", + "RADIUS_OUT_OF_RANGE", + "SOIL_FACTOR_ROUNDS" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1025/reference/crane_hire_calc.clas.abap b/tasks_gen/train/G1025/reference/crane_hire_calc.clas.abap new file mode 100644 index 0000000..3806b0a --- /dev/null +++ b/tasks_gen/train/G1025/reference/crane_hire_calc.clas.abap @@ -0,0 +1,124 @@ +CLASS {{p}}crane_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_crane_hire. + + PRIVATE SECTION. + CONSTANTS: + c_min_radius TYPE i VALUE 5, + c_max_radius TYPE i VALUE 60, + c_min_load TYPE i VALUE 500, + c_permit_fee TYPE i VALUE 250, + c_min_fee TYPE i VALUE 300, + c_radius_rate TYPE i VALUE 15, + c_load_rate TYPE i VALUE 5. + + TYPES: + BEGIN OF ty_band, + radius_max TYPE i, + max_load TYPE i, + END OF ty_band, + tt_band TYPE STANDARD TABLE OF ty_band WITH EMPTY KEY. + + METHODS validate + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + RETURNING VALUE(rv_reason) TYPE {{p}}if_crane_hire=>ty_reason. + METHODS max_load + IMPORTING iv_radius_m TYPE i + RETURNING VALUE(rv_load) TYPE i. + METHODS load_chart + RETURNING VALUE(rt_band) TYPE tt_band. + METHODS ground_factor + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_percent) TYPE i. + METHODS normalize_ground + IMPORTING iv_ground TYPE string + RETURNING VALUE(rv_ground) TYPE string. + METHODS apply_percent + IMPORTING iv_value TYPE i + iv_percent TYPE i + RETURNING VALUE(rv_value) TYPE i. +ENDCLASS. + + +CLASS {{p}}crane_hire_calc IMPLEMENTATION. + METHOD {{p}}if_crane_hire~hire. + DATA(lv_reason) = validate( iv_radius_m = iv_radius_m + iv_load_kg = iv_load_kg + iv_ground = iv_ground ). + IF lv_reason <> {{p}}if_crane_hire=>c_reason-ok. + rs_result = VALUE #( reason = lv_reason fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_base) = iv_radius_m * c_radius_rate + ( iv_load_kg DIV 100 ) * c_load_rate. + DATA(lv_fee) = apply_percent( iv_value = lv_base + iv_percent = ground_factor( iv_ground ) ). + IF iv_permit = abap_true. + lv_fee = lv_fee + c_permit_fee. + ENDIF. + IF lv_fee < c_min_fee. + lv_fee = c_min_fee. + ENDIF. + + rs_result = VALUE #( reason = {{p}}if_crane_hire=>c_reason-ok fee = lv_fee ). + ENDMETHOD. + + METHOD validate. + IF iv_radius_m < c_min_radius OR iv_radius_m > c_max_radius. + rv_reason = {{p}}if_crane_hire=>c_reason-radius. + RETURN. + ENDIF. + IF iv_load_kg < c_min_load. + rv_reason = {{p}}if_crane_hire=>c_reason-load. + RETURN. + ENDIF. + IF iv_load_kg > max_load( iv_radius_m ). + rv_reason = {{p}}if_crane_hire=>c_reason-capacity. + RETURN. + ENDIF. + IF ground_factor( iv_ground ) = 0. + rv_reason = {{p}}if_crane_hire=>c_reason-ground. + RETURN. + ENDIF. + rv_reason = {{p}}if_crane_hire=>c_reason-ok. + ENDMETHOD. + + METHOD max_load. + DATA(lt_band) = load_chart( ). + LOOP AT lt_band INTO DATA(ls_band). + IF iv_radius_m <= ls_band-radius_max. + rv_load = ls_band-max_load. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD load_chart. + rt_band = VALUE #( ( radius_max = 10 max_load = 12000 ) + ( radius_max = 20 max_load = 8000 ) + ( radius_max = 30 max_load = 5000 ) + ( radius_max = 45 max_load = 3000 ) + ( radius_max = 60 max_load = 1500 ) ). + ENDMETHOD. + + METHOD ground_factor. + CASE normalize_ground( iv_ground ). + WHEN 'ROAD'. + rv_percent = 100. + WHEN 'GRAVEL'. + rv_percent = 115. + WHEN 'SOIL'. + rv_percent = 135. + ENDCASE. + ENDMETHOD. + + METHOD normalize_ground. + rv_ground = to_upper( condense( iv_ground ) ). + ENDMETHOD. + + METHOD apply_percent. + rv_value = ( iv_value * iv_percent + 50 ) DIV 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/reference/crane_hire_calc.testclasses.abap b/tasks_gen/train/G1025/reference/crane_hire_calc.testclasses.abap new file mode 100644 index 0000000..cb1cc99 --- /dev/null +++ b/tasks_gen/train/G1025/reference/crane_hire_calc.testclasses.abap @@ -0,0 +1,104 @@ +CLASS ltc_crane_hire DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_crane_hire. + METHODS setup. + METHODS road_fee FOR TESTING. + METHODS gravel_fee FOR TESTING. + METHODS soil_fee FOR TESTING. + METHODS permit_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS invalid_radius FOR TESTING. + METHODS invalid_load FOR TESTING. + METHODS over_capacity FOR TESTING. + METHODS invalid_ground FOR TESTING. +ENDCLASS. + + +CLASS ltc_crane_hire IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}crane_hire_calc( ). + ENDMETHOD. + + METHOD road_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = 550 + act = ls_result-fee ). + ENDMETHOD. + + METHOD gravel_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 30 + iv_load_kg = 5000 + iv_ground = `GRAVEL` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 805 + act = ls_result-fee ). + ENDMETHOD. + + METHOD soil_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 10 + iv_load_kg = 12000 + iv_ground = `SOIL` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 1013 + act = ls_result-fee ). + ENDMETHOD. + + METHOD permit_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 800 + act = ls_result-fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 5 + iv_load_kg = 500 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 300 + act = ls_result-fee ). + ENDMETHOD. + + METHOD invalid_radius. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 61 + iv_load_kg = 5000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-radius + act = ls_result-reason ). + ENDMETHOD. + + METHOD invalid_load. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 499 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-load + act = ls_result-reason ). + ENDMETHOD. + + METHOD over_capacity. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 25 + iv_load_kg = 6000 + iv_ground = `ROAD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-capacity + act = ls_result-reason ). + ENDMETHOD. + + METHOD invalid_ground. + DATA(ls_result) = mo_cut->hire( iv_radius_m = 20 + iv_load_kg = 5000 + iv_ground = `FIELD` + iv_permit = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_crane_hire=>c_reason-ground + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1025/seed/if_crane_hire.intf.abap b/tasks_gen/train/G1025/seed/if_crane_hire.intf.abap new file mode 100644 index 0000000..84cb6ac --- /dev/null +++ b/tasks_gen/train/G1025/seed/if_crane_hire.intf.abap @@ -0,0 +1,25 @@ +INTERFACE {{p}}if_crane_hire PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + fee TYPE i, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + radius TYPE ty_reason VALUE 'RADIUS', + load TYPE ty_reason VALUE 'LOAD', + capacity TYPE ty_reason VALUE 'CAPACITY', + ground TYPE ty_reason VALUE 'GROUND', + END OF c_reason. + + METHODS hire + IMPORTING iv_radius_m TYPE i + iv_load_kg TYPE i + iv_ground TYPE string + iv_permit TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1025/spec.md b/tasks_gen/train/G1025/spec.md new file mode 100644 index 0000000..f3192e6 --- /dev/null +++ b/tasks_gen/train/G1025/spec.md @@ -0,0 +1,54 @@ +# 1. Goal +Calculate the hire fee of a mobile crane job. A quotation program calls the +calculation before it sends a quote to the customer. The program needs the fee +and one reason code for each request. + +# 2. Open questions +None. + +# 3. Context +- Interface {{P}}IF_CRANE_HIRE exists in package $TMP. It is active. +- Read the interface. It contains the types, the reason codes and the signature + of the calculation method. Use the names of the interface. +- The method receives the working radius, the load, the ground type and a flag + for a road permit. It returns a reason code and a fee. + +# 4. Contract +- Create the class {{P}}CRANE_HIRE_CALC in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class implements {{P}}IF_CRANE_HIRE. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. If the radius is less than 5 or more than 60, return the radius reason and a + fee of 0. +2. If the load is less than 500, return the load reason and a fee of 0. +3. The crane has a load chart. The maximum load depends on the radius: + radius up to 10 m: 12000 kg, up to 20 m: 8000 kg, up to 30 m: 5000 kg, + up to 45 m: 3000 kg, up to 60 m: 1500 kg. + If the load is more than the maximum load for the radius, return the capacity + reason and a fee of 0. +4. Remove the leading and the trailing spaces from the ground type. Convert it + to upper case. The ground type must be ROAD, GRAVEL or SOIL. If not, return + the ground reason and a fee of 0. +5. Calculate the base fee: the radius in metres multiplied by 15, plus the load + in kilograms divided by 100 and rounded down, multiplied by 5. +6. Multiply the base fee by the ground factor and divide the result by 100. The + ground factors are: ROAD 100, GRAVEL 115, SOIL 135. +7. If the road permit is required, add 250 to the fee. +8. If the fee is less than 300, set the fee to 300. +9. Return the OK reason and the fee. +Round each multiplication by a ground factor to the nearest whole euro. Round +halves up. The fee is a whole number of euros. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_CRANE_HIRE. + +# 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/G1025/task.json b/tasks_gen/train/G1025/task.json new file mode 100644 index 0000000..3f841ae --- /dev/null +++ b/tasks_gen/train/G1025/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1025", + "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_CRANE_HIRE", + "file": "seed/if_crane_hire.intf.abap", + "description": "Crane hire contract: types, reason codes and method signature" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CRANE_HIRE_CALC", + "implements": "{{P}}IF_CRANE_HIRE" + } + ], + "out_of_scope": [ + "{{P}}IF_CRANE_HIRE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the crane hire fee" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CRANE_HIRE_CALC", + "file": "reference/crane_hire_calc.clas.abap", + "description": "Crane hire fee calculator", + "testclasses_file": "reference/crane_hire_calc.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1026/faulty/m0_guard_fee.func.abap b/tasks_gen/train/G1026/faulty/m0_guard_fee.func.abap new file mode 100644 index 0000000..30e3d97 --- /dev/null +++ b/tasks_gen/train/G1026/faulty/m0_guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_true. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/faulty/m1_guard_fee.func.abap b/tasks_gen/train/G1026/faulty/m1_guard_fee.func.abap new file mode 100644 index 0000000..8fa26d4 --- /dev/null +++ b/tasks_gen/train/G1026/faulty/m1_guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours > 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/faulty/m2_guard_fee.func.abap b/tasks_gen/train/G1026/faulty/m2_guard_fee.func.abap new file mode 100644 index 0000000..7d35654 --- /dev/null +++ b/tasks_gen/train/G1026/faulty/m2_guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 3 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/faulty/m3_guard_fee.func.abap b/tasks_gen/train/G1026/faulty/m3_guard_fee.func.abap new file mode 100644 index 0000000..0935e95 --- /dev/null +++ b/tasks_gen/train/G1026/faulty/m3_guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate / c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/faulty/m4_guard_fee.func.abap b/tasks_gen/train/G1026/faulty/m4_guard_fee.func.abap new file mode 100644 index 0000000..f626d2e --- /dev/null +++ b/tasks_gen/train/G1026/faulty/m4_guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true OR cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/generation.json b/tasks_gen/train/G1026/generation.json new file mode 100644 index 0000000..dd45f57 --- /dev/null +++ b/tasks_gen/train/G1026/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1026", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t88_test.clas.abap line 64: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 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/G1026/hidden/t88_hidden.clas.abap b/tasks_gen/train/G1026/hidden/t88_hidden.clas.abap new file mode 100644 index 0000000..84e8951 --- /dev/null +++ b/tasks_gen/train/G1026/hidden/t88_hidden.clas.abap @@ -0,0 +1,129 @@ +CLASS {{p}}t88_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_type TYPE string + iv_hours TYPE decfloat34 + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS day_shift_fee FOR TESTING. + METHODS day_rounding FOR TESTING. + METHODS day_minimum_fee FOR TESTING. + METHODS night_shift_fee FOR TESTING. + METHODS night_rounding FOR TESTING. + METHODS holiday_shift_fee FOR TESTING. + METHODS holiday_above_min FOR TESTING. + METHODS other_shift_zero FOR TESTING. + METHODS zero_hours_rejected FOR TESTING. + METHODS negative_hours_rejected FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t88_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = iv_type + iv_hours = iv_hours + IMPORTING + ev_surcharge = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD day_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '102.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD day_rounding. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '57.38'. + call( EXPORTING iv_type = 'DAY' iv_hours = '2.25' + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD day_minimum_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '50.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 1 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD night_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '127.50'. + call( EXPORTING iv_type = 'NIGHT' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + ENDMETHOD. + + METHOD night_rounding. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '71.72'. + call( EXPORTING iv_type = 'NIGHT' iv_hours = '2.25' + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD holiday_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '204.00'. + call( EXPORTING iv_type = 'HOLIDAY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + ENDMETHOD. + + METHOD holiday_above_min. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '51.00'. + call( EXPORTING iv_type = 'HOLIDAY' iv_hours = 1 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD other_shift_zero. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '0.00'. + call( EXPORTING iv_type = 'STANDBY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD zero_hours_rejected. + DATA lv_fee TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + DATA lv_exp TYPE decfloat34 VALUE '7.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 0 iv_fee = 7 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD negative_hours_rejected. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_type = 'NIGHT' iv_hours = '-2.5' + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1026/mutation.json b/tasks_gen/train/G1026/mutation.json new file mode 100644 index 0000000..838fd80 --- /dev/null +++ b/tasks_gen/train/G1026/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1026", + "mutants": [ + { + "object": "{{P}}GUARD_FEE", + "mutant": "line 19: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "DAY_SHIFT_FEE", + "OTHER_SHIFT_ZERO" + ] + }, + { + "object": "{{P}}GUARD_FEE", + "mutant": "line 21: <= -> > (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "DAY_MINIMUM_FEE", + "DAY_ROUNDING", + "DAY_SHIFT_FEE", + "HOLIDAY_ABOVE_MIN", + "HOLIDAY_SHIFT_FEE", + "NEGATIVE_HOURS_REJECTED", + "NIGHT_ROUNDING", + "NIGHT_SHIFT_FEE", + "OTHER_SHIFT_ZERO", + "ZERO_HOURS_REJECTED" + ] + }, + { + "object": "{{P}}GUARD_FEE", + "mutant": "line 32: 2 -> 3 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "NIGHT_ROUNDING" + ] + }, + { + "object": "{{P}}GUARD_FEE", + "mutant": "line 36: * -> / (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "HOLIDAY_ABOVE_MIN", + "HOLIDAY_SHIFT_FEE" + ] + }, + { + "object": "{{P}}GUARD_FEE", + "mutant": "line 41: AND -> OR (logic)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "DAY_ROUNDING", + "DAY_SHIFT_FEE", + "HOLIDAY_ABOVE_MIN", + "HOLIDAY_SHIFT_FEE", + "NIGHT_ROUNDING", + "NIGHT_SHIFT_FEE", + "OTHER_SHIFT_ZERO" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1026/reference/guard_fee.func.abap b/tasks_gen/train/G1026/reference/guard_fee.func.abap new file mode 100644 index 0000000..7a765bd --- /dev/null +++ b/tasks_gen/train/G1026/reference/guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/reference/t88_test.clas.abap b/tasks_gen/train/G1026/reference/t88_test.clas.abap new file mode 100644 index 0000000..dd21afb --- /dev/null +++ b/tasks_gen/train/G1026/reference/t88_test.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}t88_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_type TYPE string + iv_hours TYPE decfloat34 + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS day_fee FOR TESTING. + METHODS night_fee FOR TESTING. + METHODS holiday_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS invalid_hours FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t88_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = iv_type + iv_hours = iv_hours + IMPORTING + ev_surcharge = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 0 act = sy-subrc ). + ENDMETHOD. + + METHOD day_fee. + DATA lv_exp TYPE decfloat34 VALUE '102.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'DAY' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD night_fee. + DATA lv_exp TYPE decfloat34 VALUE '127.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'NIGHT' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD holiday_fee. + DATA lv_exp TYPE decfloat34 VALUE '204.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'HOLIDAY' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_exp TYPE decfloat34 VALUE '50.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'DAY' iv_hours = 1 ) ). + ENDMETHOD. + + METHOD invalid_hours. + DATA lv_fee TYPE decfloat34 VALUE '7.00'. + DATA lv_expected TYPE decfloat34 VALUE '7.00'. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = 'DAY' + iv_hours = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1026/seed/guard_sample.func.abap b/tasks_gen/train/G1026/seed/guard_sample.func.abap new file mode 100644 index 0000000..ad4ee7f --- /dev/null +++ b/tasks_gen/train/G1026/seed/guard_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}guard_sample + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + ev_surcharge = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1026/spec.md b/tasks_gen/train/G1026/spec.md new file mode 100644 index 0000000..b9a51ef --- /dev/null +++ b/tasks_gen/train/G1026/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A security company bills its clients for guard shifts. The billing program calls +one function module for each shift. The function module calculates the fee of +the shift. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_GUARD exists in package $TMP. +- Function module {{P}}GUARD_SAMPLE exists in this function group. It is the + sample module. The billing program calls all shift modules with the same + interface. + +# 4. Contract +- Create the function module {{P}}GUARD_FEE in function group {{P}}FG_GUARD. +- Use exactly the interface of {{P}}GUARD_SAMPLE: the same parameter names, + parameter kinds, types and exceptions. + +# 5. Business rules +1. If iv_hours is less than or equal to 0, raise the exception INVALID_HOURS. + Do not change cv_fee. +2. Set ev_surcharge to false. +3. Shift type DAY: the fee is iv_hours multiplied by 25.50. +4. Shift type NIGHT: the fee is iv_hours multiplied by 25.50 multiplied by 1.25. + Set ev_surcharge to true. +5. Shift type HOLIDAY: the fee is iv_hours multiplied by 25.50 multiplied by + 2.00. Set ev_surcharge to true. +6. Any other shift type: the fee is 0. Rules 7 and 8 do not apply. +7. For the shift types DAY, NIGHT and HOLIDAY, round the fee half up to 2 + decimal places. +8. If the fee of rule 7 is less than 50.00, set the fee to 50.00. + +# 6. Constraints +- Release target: 7.02. Use only syntax that release 7.02 supports. +- Package $TMP. Do not use transports. +- Out of scope: do not change {{P}}GUARD_SAMPLE. +- Do not use application tables. + +# 7. Acceptance +- The function module {{P}}GUARD_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/G1026/task.json b/tasks_gen/train/G1026/task.json new file mode 100644 index 0000000..b62d3f9 --- /dev/null +++ b/tasks_gen/train/G1026/task.json @@ -0,0 +1,81 @@ +{ + "id": "G1026", + "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_GUARD", + "description": "Guard shift function group" + }, + { + "type": "FUNC", + "name": "{{P}}GUARD_SAMPLE", + "functionGroup": "{{P}}FG_GUARD", + "file": "seed/guard_sample.func.abap", + "description": "Sample shift module that fixes the interface" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}GUARD_FEE", + "functionGroup": "{{P}}FG_GUARD", + "params": [ + { + "name": "iv_shift_type", + "type": "string" + }, + { + "name": "iv_hours", + "type": "decfloat34" + }, + { + "name": "ev_surcharge", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}GUARD_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T88_HIDDEN", + "file": "hidden/t88_hidden.clas.abap", + "description": "T88 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}GUARD_FEE", + "functionGroup": "{{P}}FG_GUARD", + "file": "reference/guard_fee.func.abap", + "description": "Guard shift fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T88_TEST", + "file": "reference/t88_test.clas.abap", + "description": "T88 own tests" + } + ], + "craft_checks": [ + "method_length", + "release_syntax" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1027/generation.json b/tasks_gen/train/G1027/generation.json new file mode 100644 index 0000000..57ae60e --- /dev/null +++ b/tasks_gen/train/G1027/generation.json @@ -0,0 +1,30 @@ +{ + "id": "G1027", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/scaffold_fee.testclasses.abap line 19: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 19: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"20\"", + "reference/scaffold_fee.testclasses.abap line 25: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 25: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"41\"", + "reference/scaffold_fee.testclasses.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"06\"", + "reference/scaffold_fee.testclasses.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"94\"" + ], + "by_check": { + "abaplint": 8 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1027/reference/scaffold_fee.clas.abap b/tasks_gen/train/G1027/reference/scaffold_fee.clas.abap new file mode 100644 index 0000000..018da76 --- /dev/null +++ b/tasks_gen/train/G1027/reference/scaffold_fee.clas.abap @@ -0,0 +1,54 @@ +CLASS {{p}}scaffold_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hire_fee. + + PRIVATE SECTION. + TYPES ty_exact TYPE p LENGTH 15 DECIMALS 5. + + CONSTANTS: + c_daily_rate TYPE {{p}}if_hire_fee=>ty_amount VALUE '4.20', + c_express_percent TYPE i VALUE 12, + c_discount_percent TYPE i VALUE 6, + c_long_hire_days TYPE i VALUE 30. + + METHODS base_fee + IMPORTING iv_days TYPE i + iv_parts TYPE i + RETURNING VALUE(rv_fee) TYPE {{p}}if_hire_fee=>ty_amount. + METHODS express_surcharge + IMPORTING iv_base TYPE {{p}}if_hire_fee=>ty_amount + RETURNING VALUE(rv_surcharge) TYPE {{p}}if_hire_fee=>ty_amount. + METHODS long_hire_discount + IMPORTING iv_base TYPE {{p}}if_hire_fee=>ty_amount + RETURNING VALUE(rv_discount) TYPE {{p}}if_hire_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}scaffold_fee IMPLEMENTATION. + METHOD {{p}}if_hire_fee~calculate_fee. + DATA(lv_base) = base_fee( iv_days = iv_days iv_parts = iv_parts ). + DATA(lv_fee) = lv_base. + IF iv_express = abap_true. + lv_fee = lv_fee + express_surcharge( lv_base ). + ENDIF. + IF iv_days > c_long_hire_days. + rs_result-discount = long_hire_discount( lv_base ). + lv_fee = lv_fee - rs_result-discount. + ENDIF. + rs_result-fee = lv_fee. + ENDMETHOD. + + METHOD base_fee. + rv_fee = iv_days * iv_parts * c_daily_rate. + ENDMETHOD. + + METHOD express_surcharge. + DATA(lv_amount) = CONV ty_exact( iv_base ) * c_express_percent / 100. + rv_surcharge = round( val = lv_amount dec = 2 mode = 'CEILING' ). + ENDMETHOD. + + METHOD long_hire_discount. + DATA(lv_amount) = CONV ty_exact( iv_base ) * c_discount_percent / 100. + rv_discount = round( val = lv_amount dec = 2 mode = 'HALF_UP' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1027/reference/scaffold_fee.testclasses.abap b/tasks_gen/train/G1027/reference/scaffold_fee.testclasses.abap new file mode 100644 index 0000000..1f60694 --- /dev/null +++ b/tasks_gen/train/G1027/reference/scaffold_fee.testclasses.abap @@ -0,0 +1,52 @@ +CLASS ltc_scaffold_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}scaffold_fee. + METHODS setup. + METHODS base_fee_only FOR TESTING. + METHODS express_surcharge_rounds_up FOR TESTING. + METHODS discount_on_long_hire FOR TESTING. + METHODS no_discount_on_short_hire FOR TESTING. +ENDCLASS. + + +CLASS ltc_scaffold_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}scaffold_fee( ). + ENDMETHOD. + + METHOD base_fee_only. + DATA lv_exp TYPE {{p}}if_hire_fee=>ty_amount. + DATA(ls_result) = mo_cut->{{p}}if_hire_fee~calculate_fee( + iv_days = 3 iv_parts = 2 iv_express = abap_false ). + lv_exp = '25.20'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-fee ). + lv_exp = '0.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-discount ). + ENDMETHOD. + + METHOD express_surcharge_rounds_up. + DATA lv_exp TYPE {{p}}if_hire_fee=>ty_amount. + DATA(ls_result) = mo_cut->{{p}}if_hire_fee~calculate_fee( + iv_days = 2 iv_parts = 1 iv_express = abap_true ). + lv_exp = '9.41'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-fee ). + ENDMETHOD. + + METHOD discount_on_long_hire. + DATA lv_exp TYPE {{p}}if_hire_fee=>ty_amount. + DATA(ls_result) = mo_cut->{{p}}if_hire_fee~calculate_fee( + iv_days = 31 iv_parts = 5 iv_express = abap_false ). + lv_exp = '39.06'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-discount ). + lv_exp = '611.94'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-fee ). + ENDMETHOD. + + METHOD no_discount_on_short_hire. + DATA lv_exp TYPE {{p}}if_hire_fee=>ty_amount. + DATA(ls_result) = mo_cut->{{p}}if_hire_fee~calculate_fee( + iv_days = 30 iv_parts = 1 iv_express = abap_false ). + lv_exp = '0.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = ls_result-discount ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1027/seed/if_hire_fee.intf.abap b/tasks_gen/train/G1027/seed/if_hire_fee.intf.abap new file mode 100644 index 0000000..50dbdc8 --- /dev/null +++ b/tasks_gen/train/G1027/seed/if_hire_fee.intf.abap @@ -0,0 +1,13 @@ +INTERFACE {{p}}if_hire_fee PUBLIC. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + fee TYPE ty_amount, + discount TYPE ty_amount, + END OF ty_result. + METHODS calculate_fee + IMPORTING iv_days TYPE i + iv_parts TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1027/spec.md b/tasks_gen/train/G1027/spec.md new file mode 100644 index 0000000..8fab93f --- /dev/null +++ b/tasks_gen/train/G1027/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A scaffolding company hires out scaffold parts to building sites. A quotation +program calls the fee calculation for each hire order before it prints the +quotation. The program needs the final fee and the long-hire discount of the +hire order. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_HIRE_FEE exists in package $TMP. It is active. +- The interface has the type TY_AMOUNT (packed, length 8, 2 decimals) and the + structure type TY_RESULT with the components FEE and DISCOUNT. Both + components have the type TY_AMOUNT. +- The interface has one method: + CALCULATE_FEE + IMPORTING iv_days TYPE i + iv_parts TYPE i + iv_express TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}SCAFFOLD_FEE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_HIRE_FEE. +- Do not add other public methods. + +# 5. Business rules +1. The daily rate of one scaffold part is 4.20 EUR. +2. The base fee is IV_DAYS multiplied by IV_PARTS multiplied by the daily rate. +3. If IV_EXPRESS is true, the express surcharge is 12 percent of the base fee. + Round the express surcharge up to the next full cent. +4. If IV_DAYS is greater than 30, the long-hire discount is 6 percent of the + base fee. +5. The final fee is the base fee plus the express surcharge minus the long-hire + discount. +6. The result contains the final fee in the component FEE and the long-hire + discount in the component DISCOUNT. +7. The final fee and the discount are amounts in EUR. Each amount is exact to + the cent. +8. If IV_DAYS is not greater than 30, the discount is zero. + +# 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_HIRE_FEE. + +# 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/G1027/task.json b/tasks_gen/train/G1027/task.json new file mode 100644 index 0000000..ba963ae --- /dev/null +++ b/tasks_gen/train/G1027/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1027", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_HIRE_FEE", + "file": "seed/if_hire_fee.intf.abap", + "description": "Hire fee contract with the result structure" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SCAFFOLD_FEE", + "implements": "{{P}}IF_HIRE_FEE" + } + ], + "out_of_scope": [ + "{{P}}IF_HIRE_FEE" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SCAFFOLD_FEE", + "file": "reference/scaffold_fee.clas.abap", + "description": "Scaffold hire fee calculation", + "testclasses_file": "reference/scaffold_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ], + "expected_gap": { + "description": "The business rules do not say how to round the long-hire discount to full cents, so the returned discount and the final fee cannot be computed.", + "keywords": [ + [ + "discount", + "round" + ], + [ + "discount", + "cent" + ], + [ + "round", + "rule" + ] + ] + } +} \ No newline at end of file diff --git a/tasks_gen/train/G1028/generation.json b/tasks_gen/train/G1028/generation.json new file mode 100644 index 0000000..31740dc --- /dev/null +++ b/tasks_gen/train/G1028/generation.json @@ -0,0 +1,50 @@ +{ + "id": "G1028", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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/G1028/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1028/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..a800150 --- /dev/null +++ b/tasks_gen/train/G1028/hidden/t20_hidden.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}t20_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_departments FOR TESTING. + METHODS parent_name FOR TESTING. + METHODS top_has_no_parent FOR TESTING. + METHODS orphan_has_no_parent FOR TESTING. + METHODS own_data_shown FOR TESTING. + METHODS headcount_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_DEPT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_dept TYPE STANDARD TABLE OF {{p}}dept WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_dept = VALUE #( + ( dept_id = 'D100' dept_name = 'HEAD OFFICE' parent_id = '' headcount = 10 ) + ( dept_id = 'D200' dept_name = 'SALES' parent_id = 'D100' headcount = 25 ) + ( dept_id = 'D300' dept_name = 'PURCHASING' parent_id = 'D100' headcount = 12 ) + ( dept_id = 'D400' dept_name = 'EXPORT' parent_id = 'D200' headcount = 8 ) + ( dept_id = 'D500' dept_name = 'ORPHAN' parent_id = 'D999' headcount = 3 ) ). + go_env->insert_test_data( lt_dept ). + ENDMETHOD. + + METHOD all_departments. + SELECT dept_id FROM {{p}}i_dept INTO TABLE @DATA(lt_ids). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_ids ) ). + ENDMETHOD. + + METHOD parent_name. + SELECT SINGLE _parent~dept_name FROM {{p}}i_dept + WHERE dept_id = 'D200' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'HEAD OFFICE' act = lv_name ). + ENDMETHOD. + + METHOD top_has_no_parent. + SELECT SINGLE _parent~dept_name FROM {{p}}i_dept + WHERE dept_id = 'D100' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_initial( act = lv_name ). + ENDMETHOD. + + METHOD orphan_has_no_parent. + SELECT SINGLE _parent~dept_name FROM {{p}}i_dept + WHERE dept_id = 'D500' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_initial( act = lv_name ). + ENDMETHOD. + + METHOD own_data_shown. + SELECT SINGLE dept_name parent_id FROM {{p}}i_dept + WHERE dept_id = 'D400' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 'EXPORT' act = ls_row-dept_name ). + cl_abap_unit_assert=>assert_equals( exp = 'D200' act = ls_row-parent_id ). + ENDMETHOD. + + METHOD headcount_shown. + SELECT SINGLE headcount FROM {{p}}i_dept + WHERE dept_id = 'D300' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 12 act = lv_count ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1028/mutation.json b/tasks_gen/train/G1028/mutation.json new file mode 100644 index 0000000..1db7c84 --- /dev/null +++ b/tasks_gen/train/G1028/mutation.json @@ -0,0 +1,39 @@ +{ + "task": "T20", + "mutants": [ + { + "object": "{{P}}I_DEPT", + "mutant": "line 5: association on: $projection.parent_id -> $projection.dept_id (assoc on)", + "status": "killed", + "hidden": "3/6", + "failed_tests": [ + "ORPHAN_HAS_NO_PARENT", + "PARENT_NAME", + "TOP_HAS_NO_PARENT" + ] + }, + { + "object": "{{P}}I_DEPT", + "mutant": "line 10: headcount -> dept_id (element)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "HEADCOUNT_SHOWN" + ] + }, + { + "object": "{{P}}I_DEPT", + "mutant": "line 8: dept_name -> dept_id (element)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "OWN_DATA_SHOWN", + "PARENT_NAME" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1028/reference/i_dept.ddls.asddls b/tasks_gen/train/G1028/reference/i_dept.ddls.asddls new file mode 100644 index 0000000..c214d00 --- /dev/null +++ b/tasks_gen/train/G1028/reference/i_dept.ddls.asddls @@ -0,0 +1,12 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Departments' +define view entity {{P}}I_DEPT + as select from {{p}}dept + association [0..1] to {{p}}dept as _parent on $projection.parent_id = _parent.dept_id +{ + key dept_id, + dept_name, + parent_id, + headcount, + _parent +} diff --git a/tasks_gen/train/G1028/reference/t20_test.clas.abap b/tasks_gen/train/G1028/reference/t20_test.clas.abap new file mode 100644 index 0000000..0497152 --- /dev/null +++ b/tasks_gen/train/G1028/reference/t20_test.clas.abap @@ -0,0 +1,35 @@ +CLASS {{p}}t20_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 parent_name_via_assoc FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_DEPT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_dept TYPE STANDARD TABLE OF {{p}}dept WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_dept = VALUE #( + ( dept_id = 'D100' dept_name = 'HEAD OFFICE' parent_id = '' headcount = 10 ) + ( dept_id = 'D200' dept_name = 'SALES' parent_id = 'D100' headcount = 25 ) ). + go_env->insert_test_data( lt_dept ). + ENDMETHOD. + + METHOD parent_name_via_assoc. + SELECT SINGLE _parent~dept_name FROM {{p}}i_dept + WHERE dept_id = 'D200' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'HEAD OFFICE' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1028/seed/dept.tabl.asabap b/tasks_gen/train/G1028/seed/dept.tabl.asabap new file mode 100644 index 0000000..0f68e29 --- /dev/null +++ b/tasks_gen/train/G1028/seed/dept.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Department' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}dept { + key client : abap.clnt not null; + key dept_id : abap.char(8) not null; + dept_name : abap.char(30); + parent_id : abap.char(8); + headcount : abap.int4; +} diff --git a/tasks_gen/train/G1028/seed/seed_dept.clas.abap b/tasks_gen/train/G1028/seed/seed_dept.clas.abap new file mode 100644 index 0000000..d250560 --- /dev/null +++ b/tasks_gen/train/G1028/seed/seed_dept.clas.abap @@ -0,0 +1,20 @@ +CLASS {{p}}seed_dept DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_dept IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_dept TYPE STANDARD TABLE OF {{p}}dept WITH EMPTY KEY. + lt_dept = VALUE #( + ( client = sy-mandt dept_id = 'D100' dept_name = 'HEAD OFFICE' parent_id = '' headcount = 10 ) + ( client = sy-mandt dept_id = 'D200' dept_name = 'SALES' parent_id = 'D100' headcount = 25 ) + ( client = sy-mandt dept_id = 'D300' dept_name = 'PURCHASING' parent_id = 'D100' headcount = 12 ) + ( client = sy-mandt dept_id = 'D400' dept_name = 'EXPORT' parent_id = 'D200' headcount = 8 ) + ( client = sy-mandt dept_id = 'D500' dept_name = 'ORPHAN' parent_id = 'D999' headcount = 3 ) ). + DELETE FROM {{p}}dept. + INSERT {{p}}dept FROM TABLE @lt_dept. + out->write( |Rows in {{P}}DEPT: { lines( lt_dept ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1028/spec.md b/tasks_gen/train/G1028/spec.md new file mode 100644 index 0000000..173191f --- /dev/null +++ b/tasks_gen/train/G1028/spec.md @@ -0,0 +1,46 @@ +# 1. Goal +A company groups its departments in a hierarchy. Each department can have a +parent department. A report and an OData service read the departments and also +need the data of the parent department. The data must come from the database, +from a CDS view entity. An ABAP SQL consumer reads the parent data with a path +expression. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}DEPT (department), package $TMP. Key: DEPT_ID (CHAR 8). + Fields: DEPT_NAME (CHAR 30), PARENT_ID (CHAR 8), HEADCOUNT (INT4). +- PARENT_ID holds the DEPT_ID of the parent department. The top department has + an empty PARENT_ID. A PARENT_ID can also point to a department that does not + exist. + +# 4. Contract +- Create the CDS view entity {{P}}I_DEPT in package $TMP. +- Elements, with these names: DEPT_ID (key), DEPT_NAME, PARENT_ID, HEADCOUNT. +- The view has an association with the name _PARENT. The association points to + the department data. Through the association, a consumer can read the + DEPT_NAME and the other elements of the parent department. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each department in {{P}}DEPT. +2. DEPT_ID is the key element of the view. +3. The association _PARENT matches the department whose DEPT_ID is the PARENT_ID + of the current department. A department has at most one parent department. +4. A department with an empty PARENT_ID has no parent department. Reading the + parent's DEPT_NAME through the association gives an empty value. +5. A department whose PARENT_ID does not match any department has no parent + department. Reading the parent's DEPT_NAME through the association gives an + empty value. +6. DEPT_NAME, PARENT_ID and HEADCOUNT show the values of the department itself. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}DEPT and {{P}}SEED_DEPT. + +# 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/G1028/task.json b/tasks_gen/train/G1028/task.json new file mode 100644 index 0000000..d822b28 --- /dev/null +++ b/tasks_gen/train/G1028/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1028", + "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}}DEPT", + "file": "seed/dept.tabl.asabap", + "description": "Department" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DEPT", + "file": "seed/seed_dept.clas.abap", + "description": "Seed data for {{P}}DEPT", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_DEPT", + "fields": [ + "DEPT_ID", + "DEPT_NAME", + "PARENT_ID", + "HEADCOUNT", + "_PARENT" + ] + } + ], + "out_of_scope": [ + "{{P}}DEPT", + "{{P}}SEED_DEPT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "T20 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_DEPT", + "file": "reference/i_dept.ddls.asddls", + "description": "Departments with parent association" + }, + { + "type": "CLAS", + "name": "{{P}}T20_TEST", + "file": "reference/t20_test.clas.abap", + "description": "T20 own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1029/faulty/m0_licence_merger.clas.abap b/tasks_gen/train/G1029/faulty/m0_licence_merger.clas.abap new file mode 100644 index 0000000..d2c88f7 --- /dev/null +++ b/tasks_gen/train/G1029/faulty/m0_licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) <> abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats + ls_norm-seats. + IF ls_norm-unit_price > -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + AND is_row-cost_centre IS NOT INITIAL + AND is_row-seats >= 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/faulty/m1_licence_merger.clas.abap b/tasks_gen/train/G1029/faulty/m1_licence_merger.clas.abap new file mode 100644 index 0000000..490f685 --- /dev/null +++ b/tasks_gen/train/G1029/faulty/m1_licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats - ls_norm-seats. + IF ls_norm-unit_price > -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + AND is_row-cost_centre IS NOT INITIAL + AND is_row-seats >= 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/faulty/m2_licence_merger.clas.abap b/tasks_gen/train/G1029/faulty/m2_licence_merger.clas.abap new file mode 100644 index 0000000..d529659 --- /dev/null +++ b/tasks_gen/train/G1029/faulty/m2_licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats + ls_norm-seats. + IF ls_norm-unit_price <= -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + AND is_row-cost_centre IS NOT INITIAL + AND is_row-seats >= 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/faulty/m3_licence_merger.clas.abap b/tasks_gen/train/G1029/faulty/m3_licence_merger.clas.abap new file mode 100644 index 0000000..41a4c32 --- /dev/null +++ b/tasks_gen/train/G1029/faulty/m3_licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats + ls_norm-seats. + IF ls_norm-unit_price > -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + OR is_row-cost_centre IS NOT INITIAL + AND is_row-seats >= 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/faulty/m4_licence_merger.clas.abap b/tasks_gen/train/G1029/faulty/m4_licence_merger.clas.abap new file mode 100644 index 0000000..508fc68 --- /dev/null +++ b/tasks_gen/train/G1029/faulty/m4_licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats + ls_norm-seats. + IF ls_norm-unit_price > -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + AND is_row-cost_centre IS NOT INITIAL + AND is_row-seats < 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/generation.json b/tasks_gen/train/G1029/generation.json new file mode 100644 index 0000000..89cf916 --- /dev/null +++ b/tasks_gen/train/G1029/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1029", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1029/hidden/t_lic_hidden.clas.abap b/tasks_gen/train/G1029/hidden/t_lic_hidden.clas.abap new file mode 100644 index 0000000..fd93283 --- /dev/null +++ b/tasks_gen/train/G1029/hidden/t_lic_hidden.clas.abap @@ -0,0 +1,197 @@ +CLASS {{p}}t_lic_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_licence_merge. + METHODS setup. + METHODS merges_both_lists FOR TESTING. + METHODS sums_duplicates FOR TESTING. + METHODS ignores_zero_seats FOR TESTING. + METHODS ignores_negative_seats FOR TESTING. + METHODS ignores_blank_keys FOR TESTING. + METHODS normalizes_keys FOR TESTING. + METHODS sorts_result FOR TESTING. + METHODS keeps_secondary_only FOR TESTING. + METHODS empty_lists FOR TESTING. + METHODS takes_highest_price FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_lic_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}licence_merger( ). + ENDMETHOD. + + METHOD merges_both_lists. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' ) + ( product = 'BETA' cost_centre = '2000' seats = 3 unit_price = '50.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 2 unit_price = '120.00' ) + ( product = 'GAMMA' cost_centre = '3000' seats = 4 unit_price = '10.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ). + cl_abap_unit_assert=>assert_equals( exp = 7 act = ls_row-seats ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_licence_merge=>ty_price( '120.00' ) + act = ls_row-unit_price ). + ls_row = lt_act[ 3 ]. + cl_abap_unit_assert=>assert_equals( exp = 'GAMMA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-seats ). + ENDMETHOD. + + METHOD sums_duplicates. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' ) + ( product = 'ALPHA' cost_centre = '1000' seats = 3 unit_price = '90.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 8 act = ls_row-seats ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_licence_merge=>ty_price( '100.00' ) + act = ls_row-unit_price ). + ENDMETHOD. + + METHOD ignores_zero_seats. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '100.00' ) + ( product = 'BETA' cost_centre = '2000' seats = 4 unit_price = '10.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '50.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-seats ). + ENDMETHOD. + + METHOD ignores_negative_seats. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = -3 unit_price = '100.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'BETA' cost_centre = '2000' seats = 2 unit_price = '10.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ). + ENDMETHOD. + + METHOD ignores_blank_keys. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = '' cost_centre = '1000' seats = 5 unit_price = '10.00' ) + ( product = 'ALPHA' cost_centre = '' seats = 5 unit_price = '10.00' ) + ( product = ' ' cost_centre = '2000' seats = 5 unit_price = '10.00' ) + ( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '2.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-seats ). + ENDMETHOD. + + METHOD normalizes_keys. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'alpha' cost_centre = '1000' seats = 2 unit_price = '10.00' ) + ( product = ' beta ' cost_centre = '2000' seats = 1 unit_price = '5.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 3 unit_price = '20.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = ls_row-seats ). + ls_row = lt_act[ 2 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ). + ENDMETHOD. + + METHOD sorts_result. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '1.00' ) + ( product = 'ALPHA' cost_centre = '3000' seats = 1 unit_price = '1.00' ) + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '1.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ). + ls_row = lt_act[ 2 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '3000' act = ls_row-cost_centre ). + ls_row = lt_act[ 3 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ). + ENDMETHOD. + + METHOD keeps_secondary_only. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '5.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'DELTA' cost_centre = '4000' seats = 2 unit_price = '7.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 2 ]. + cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '4000' act = ls_row-cost_centre ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ). + ENDMETHOD. + + METHOD empty_lists. + DATA(lt_empty) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_empty + it_secondary = lt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ). + ENDMETHOD. + + METHOD takes_highest_price. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '80.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '95.50' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_licence_merge=>ty_price( '95.50' ) + act = ls_row-unit_price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/mutation.json b/tasks_gen/train/G1029/mutation.json new file mode 100644 index 0000000..5560156 --- /dev/null +++ b/tasks_gen/train/G1029/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1029", + "mutants": [ + { + "object": "{{P}}LICENCE_MERGER", + "mutant": "line 41: = -> <> (eq)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "IGNORES_BLANK_KEYS", + "IGNORES_NEGATIVE_SEATS", + "IGNORES_ZERO_SEATS", + "KEEPS_SECONDARY_ONLY", + "MERGES_BOTH_LISTS", + "NORMALIZES_KEYS", + "SORTS_RESULT", + "SUMS_DUPLICATES", + "TAKES_HIGHEST_PRICE" + ] + }, + { + "object": "{{P}}LICENCE_MERGER", + "mutant": "line 49: + -> - (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "MERGES_BOTH_LISTS", + "NORMALIZES_KEYS", + "SUMS_DUPLICATES", + "TAKES_HIGHEST_PRICE" + ] + }, + { + "object": "{{P}}LICENCE_MERGER", + "mutant": "line 50: > -> <= (rel)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "MERGES_BOTH_LISTS", + "SUMS_DUPLICATES", + "TAKES_HIGHEST_PRICE" + ] + }, + { + "object": "{{P}}LICENCE_MERGER", + "mutant": "line 67: AND -> OR (logic)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "IGNORES_BLANK_KEYS", + "IGNORES_NEGATIVE_SEATS", + "IGNORES_ZERO_SEATS" + ] + }, + { + "object": "{{P}}LICENCE_MERGER", + "mutant": "line 68: >= -> < (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "IGNORES_BLANK_KEYS", + "IGNORES_NEGATIVE_SEATS", + "IGNORES_ZERO_SEATS", + "KEEPS_SECONDARY_ONLY", + "MERGES_BOTH_LISTS", + "NORMALIZES_KEYS", + "SORTS_RESULT", + "SUMS_DUPLICATES", + "TAKES_HIGHEST_PRICE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1029/reference/licence_merger.clas.abap b/tasks_gen/train/G1029/reference/licence_merger.clas.abap new file mode 100644 index 0000000..96e2512 --- /dev/null +++ b/tasks_gen/train/G1029/reference/licence_merger.clas.abap @@ -0,0 +1,77 @@ +CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_licence_merge. + + PRIVATE SECTION. + TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence + WITH UNIQUE KEY product cost_centre. + + METHODS add_rows + IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences + CHANGING ct_merged TYPE tt_merged. + + METHODS normalize + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence. + + METHODS is_relevant + IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS trim_upper + IMPORTING iv_text TYPE string + RETURNING VALUE(rv_text) TYPE string. +ENDCLASS. + + +CLASS {{p}}licence_merger IMPLEMENTATION. + METHOD {{p}}if_licence_merge~merge. + DATA lt_merged TYPE tt_merged. + + add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ). + add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ). + + rt_merged = lt_merged. + SORT rt_merged BY product cost_centre. + ENDMETHOD. + + METHOD add_rows. + LOOP AT it_rows INTO DATA(ls_row). + DATA(ls_norm) = normalize( ls_row ). + IF is_relevant( ls_norm ) = abap_false. + CONTINUE. + ENDIF. + + READ TABLE ct_merged ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY product = ls_norm-product + cost_centre = ls_norm-cost_centre. + IF sy-subrc = 0. + -seats = -seats + ls_norm-seats. + IF ls_norm-unit_price > -unit_price. + -unit_price = ls_norm-unit_price. + ENDIF. + ELSE. + INSERT ls_norm INTO TABLE ct_merged. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize. + rs_row = is_row. + rs_row-product = trim_upper( CONV string( is_row-product ) ). + rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ). + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_row-product IS NOT INITIAL + AND is_row-cost_centre IS NOT INITIAL + AND is_row-seats >= 1 ). + ENDMETHOD. + + METHOD trim_upper. + DATA(lv_text) = iv_text. + SHIFT lv_text LEFT DELETING LEADING space. + SHIFT lv_text RIGHT DELETING TRAILING space. + rv_text = to_upper( lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/reference/licence_merger.testclasses.abap b/tasks_gen/train/G1029/reference/licence_merger.testclasses.abap new file mode 100644 index 0000000..eb6e9a5 --- /dev/null +++ b/tasks_gen/train/G1029/reference/licence_merger.testclasses.abap @@ -0,0 +1,92 @@ +CLASS ltc_licence_merger DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_licence_merge. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS ignores_invalid_rows FOR TESTING. + METHODS normalizes_keys FOR TESTING. + METHODS sorts_the_result FOR TESTING. + METHODS empty_input FOR TESTING. +ENDCLASS. + + +CLASS ltc_licence_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}licence_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 2 unit_price = '120.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 7 act = ls_row-seats ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_licence_merge=>ty_price( '120.00' ) + act = ls_row-unit_price ). + ENDMETHOD. + + METHOD ignores_invalid_rows. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = '' cost_centre = '1000' seats = 5 unit_price = '10.00' ) + ( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '10.00' ) + ( product = 'BETA' cost_centre = '2000' seats = 2 unit_price = '3.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + ENDMETHOD. + + METHOD normalizes_keys. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = ' alpha ' cost_centre = '1000' seats = 1 unit_price = '2.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '2.00' ) ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ). + ENDMETHOD. + + METHOD sorts_the_result. + DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences( + ( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '1.00' ) + ( product = 'ALPHA' cost_centre = '3000' seats = 1 unit_price = '1.00' ) + ( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '1.00' ) ). + DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_primary + it_secondary = lt_secondary ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ). + DATA(ls_row) = lt_act[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ). + cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ). + ls_row = lt_act[ 3 ]. + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ). + ENDMETHOD. + + METHOD empty_input. + DATA(lt_empty) = VALUE {{p}}if_licence_merge=>tt_licences( ). + + DATA(lt_act) = mo_cut->merge( it_primary = lt_empty + it_secondary = lt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1029/seed/if_licence_merge.intf.abap b/tasks_gen/train/G1029/seed/if_licence_merge.intf.abap new file mode 100644 index 0000000..e624359 --- /dev/null +++ b/tasks_gen/train/G1029/seed/if_licence_merge.intf.abap @@ -0,0 +1,18 @@ +INTERFACE {{p}}if_licence_merge PUBLIC. + TYPES ty_product TYPE c LENGTH 10. + TYPES ty_cost_centre TYPE c LENGTH 6. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_licence, + product TYPE ty_product, + cost_centre TYPE ty_cost_centre, + seats TYPE i, + unit_price TYPE ty_price, + END OF ty_licence. + TYPES tt_licences TYPE STANDARD TABLE OF ty_licence WITH EMPTY KEY. + + METHODS merge + IMPORTING it_primary TYPE tt_licences + it_secondary TYPE tt_licences + RETURNING VALUE(rt_merged) TYPE tt_licences. +ENDINTERFACE. diff --git a/tasks_gen/train/G1029/spec.md b/tasks_gen/train/G1029/spec.md new file mode 100644 index 0000000..a74b6d8 --- /dev/null +++ b/tasks_gen/train/G1029/spec.md @@ -0,0 +1,56 @@ +# 1. Goal +An IT asset manager consolidates the software licence records of two +subsidiaries. A reporting program calls one service that merges the two lists +into one list. The result has one row for each product and cost centre. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_LICENCE_MERGE exists in package $TMP. It is active. +- The interface has the type TY_PRODUCT (character, length 10), the type + TY_COST_CENTRE (character, length 6), and the type TY_PRICE (packed number, + length 8, 2 decimals). +- The interface has the structure type TY_LICENCE with these components: + PRODUCT (TY_PRODUCT), COST_CENTRE (TY_COST_CENTRE), SEATS (integer), + UNIT_PRICE (TY_PRICE). +- The interface has the table type TT_LICENCES. TT_LICENCES is a standard table + of TY_LICENCE. +- The interface has one method: + MERGE + IMPORTING it_primary TYPE tt_licences + it_secondary TYPE tt_licences + RETURNING VALUE(rt_merged) TYPE tt_licences. + +# 4. Contract +- Create the class {{P}}LICENCE_MERGER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_LICENCE_MERGE. +- Do not add other public methods. + +# 5. Business rules +The method MERGE reads the two input tables IT_PRIMARY and IT_SECONDARY. +1. Normalize the product and the cost centre of each input row: remove the + leading and the trailing blanks and convert all letters to upper case. +2. Ignore an input row if the normalized product is empty, or if the normalized + cost centre is empty, or if the seats are less than 1. +3. All remaining input rows with the same product and the same cost centre + become one result row. Rows of the same input table and rows of different + input tables are combined in the same way. +4. The seats of a result row are the sum of the seats of its input rows. +5. The unit price of a result row is the highest unit price of its input rows. +6. The result has one row for each pair of product and cost centre that has at + least one remaining input row. +7. Sort the result by product in ascending order. If two rows have the same + product, sort them by cost centre in ascending order. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_LICENCE_MERGE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1029/task.json b/tasks_gen/train/G1029/task.json new file mode 100644 index 0000000..9928348 --- /dev/null +++ b/tasks_gen/train/G1029/task.json @@ -0,0 +1,52 @@ +{ + "id": "G1029", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_LICENCE_MERGE", + "file": "seed/if_licence_merge.intf.abap", + "description": "Licence merge contract: types and method signature" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}LICENCE_MERGER", + "implements": "{{P}}IF_LICENCE_MERGE" + } + ], + "out_of_scope": [ + "{{P}}IF_LICENCE_MERGE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_LIC_HIDDEN", + "file": "hidden/t_lic_hidden.clas.abap", + "description": "Hidden tests for the licence merger" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}LICENCE_MERGER", + "file": "reference/licence_merger.clas.abap", + "description": "Licence merger reference solution", + "testclasses_file": "reference/licence_merger.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops", + "linear_search" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1030/faulty/m0_skip_hire_calc.clas.abap b/tasks_gen/train/G1030/faulty/m0_skip_hire_calc.clas.abap new file mode 100644 index 0000000..77b395f --- /dev/null +++ b/tasks_gen/train/G1030/faulty/m0_skip_hire_calc.clas.abap @@ -0,0 +1,174 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + + PRIVATE SECTION. + + CONSTANTS c_size_small TYPE ty_size VALUE 'SMALL'. + CONSTANTS c_size_medium TYPE ty_size VALUE 'MEDIUM'. + CONSTANTS c_size_large TYPE ty_size VALUE 'LARGE'. + CONSTANTS c_base_small TYPE ty_amount VALUE '90.00'. + CONSTANTS c_base_medium TYPE ty_amount VALUE '140.00'. + CONSTANTS c_base_large TYPE ty_amount VALUE '200.00'. + CONSTANTS c_rate_small TYPE ty_amount VALUE '8.00'. + CONSTANTS c_rate_medium TYPE ty_amount VALUE '12.00'. + CONSTANTS c_rate_large TYPE ty_amount VALUE '16.00'. + CONSTANTS c_hazard_small TYPE ty_amount VALUE '60.00'. + CONSTANTS c_hazard_medium TYPE ty_amount VALUE '80.00'. + CONSTANTS c_hazard_large TYPE ty_amount VALUE '100.00'. + CONSTANTS c_permit_fee TYPE ty_amount VALUE '45.00'. + CONSTANTS c_min_charge TYPE ty_amount VALUE '120.00'. + CONSTANTS c_included_days TYPE i VALUE 7. + CONSTANTS c_min_days TYPE i VALUE 1. + CONSTANTS c_min_containers TYPE i VALUE 5. + CONSTANTS c_percent_bulk TYPE i VALUE 10. + CONSTANTS c_percent_base TYPE i VALUE 100. + + METHODS base_price + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_price) TYPE ty_amount. + + METHODS day_rate + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS hazard_fee + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS extra_days_fee + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS net_charge + IMPORTING iv_net TYPE ty_amount + iv_count TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + DATA lv_net TYPE ty_amount. + DATA lv_count TYPE i. + DATA lv_line TYPE ty_amount. + + LOOP AT it_containers INTO DATA(ls_container). + lv_line = container_charge( ls_container ). + IF lv_line > 0. + lv_net = lv_net + lv_line. + lv_count = lv_count - 1. + ENDIF. + ENDLOOP. + + rv_charge = net_charge( iv_net = lv_net iv_count = lv_count ). + ENDMETHOD. + + METHOD container_charge. + IF is_container-days < c_min_days. + RETURN. + ENDIF. + DATA(lv_base) = base_price( is_container-size ). + IF lv_base = 0. + RETURN. + ENDIF. + rv_charge = lv_base + extra_days_fee( is_container ). + IF is_container-hazardous = abap_true. + rv_charge = rv_charge + hazard_fee( is_container-size ). + ENDIF. + IF is_container-permit = abap_true. + rv_charge = rv_charge + c_permit_fee. + ENDIF. + ENDMETHOD. + + METHOD net_charge. + IF iv_net = 0. + RETURN. + ENDIF. + rv_charge = round( val = iv_net * ( c_percent_base - bulk_discount( iv_count ) ) + / c_percent_base dec = 2 ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD extra_days_fee. + DATA(lv_days) = is_container-days - c_included_days. + IF lv_days > 0. + rv_fee = day_rate( is_container-size ) * lv_days. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_size. + WHEN c_size_small. + rv_price = c_base_small. + WHEN c_size_medium. + rv_price = c_base_medium. + WHEN c_size_large. + rv_price = c_base_large. + WHEN OTHERS. + rv_price = 0. + ENDCASE. + ENDMETHOD. + + METHOD day_rate. + CASE iv_size. + WHEN c_size_small. + rv_rate = c_rate_small. + WHEN c_size_medium. + rv_rate = c_rate_medium. + WHEN c_size_large. + rv_rate = c_rate_large. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD hazard_fee. + CASE iv_size. + WHEN c_size_small. + rv_fee = c_hazard_small. + WHEN c_size_medium. + rv_fee = c_hazard_medium. + WHEN c_size_large. + rv_fee = c_hazard_large. + WHEN OTHERS. + rv_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD bulk_discount. + IF iv_count >= c_min_containers. + rv_percent = c_percent_bulk. + ELSE. + rv_percent = 0. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/faulty/m1_skip_hire_calc.clas.abap b/tasks_gen/train/G1030/faulty/m1_skip_hire_calc.clas.abap new file mode 100644 index 0000000..389a95c --- /dev/null +++ b/tasks_gen/train/G1030/faulty/m1_skip_hire_calc.clas.abap @@ -0,0 +1,174 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + + PRIVATE SECTION. + + CONSTANTS c_size_small TYPE ty_size VALUE 'SMALL'. + CONSTANTS c_size_medium TYPE ty_size VALUE 'MEDIUM'. + CONSTANTS c_size_large TYPE ty_size VALUE 'LARGE'. + CONSTANTS c_base_small TYPE ty_amount VALUE '90.00'. + CONSTANTS c_base_medium TYPE ty_amount VALUE '140.00'. + CONSTANTS c_base_large TYPE ty_amount VALUE '200.00'. + CONSTANTS c_rate_small TYPE ty_amount VALUE '8.00'. + CONSTANTS c_rate_medium TYPE ty_amount VALUE '12.00'. + CONSTANTS c_rate_large TYPE ty_amount VALUE '16.00'. + CONSTANTS c_hazard_small TYPE ty_amount VALUE '60.00'. + CONSTANTS c_hazard_medium TYPE ty_amount VALUE '80.00'. + CONSTANTS c_hazard_large TYPE ty_amount VALUE '100.00'. + CONSTANTS c_permit_fee TYPE ty_amount VALUE '45.00'. + CONSTANTS c_min_charge TYPE ty_amount VALUE '120.00'. + CONSTANTS c_included_days TYPE i VALUE 7. + CONSTANTS c_min_days TYPE i VALUE 1. + CONSTANTS c_min_containers TYPE i VALUE 5. + CONSTANTS c_percent_bulk TYPE i VALUE 10. + CONSTANTS c_percent_base TYPE i VALUE 100. + + METHODS base_price + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_price) TYPE ty_amount. + + METHODS day_rate + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS hazard_fee + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS extra_days_fee + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS net_charge + IMPORTING iv_net TYPE ty_amount + iv_count TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + DATA lv_net TYPE ty_amount. + DATA lv_count TYPE i. + DATA lv_line TYPE ty_amount. + + LOOP AT it_containers INTO DATA(ls_container). + lv_line = container_charge( ls_container ). + IF lv_line > 0. + lv_net = lv_net + lv_line. + lv_count = lv_count + 1. + ENDIF. + ENDLOOP. + + rv_charge = net_charge( iv_net = lv_net iv_count = lv_count ). + ENDMETHOD. + + METHOD container_charge. + IF is_container-days < c_min_days. + RETURN. + ENDIF. + DATA(lv_base) = base_price( is_container-size ). + IF lv_base = 0. + RETURN. + ENDIF. + rv_charge = lv_base + extra_days_fee( is_container ). + IF is_container-hazardous = abap_true. + rv_charge = rv_charge + hazard_fee( is_container-size ). + ENDIF. + IF is_container-permit = abap_false. + rv_charge = rv_charge + c_permit_fee. + ENDIF. + ENDMETHOD. + + METHOD net_charge. + IF iv_net = 0. + RETURN. + ENDIF. + rv_charge = round( val = iv_net * ( c_percent_base - bulk_discount( iv_count ) ) + / c_percent_base dec = 2 ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD extra_days_fee. + DATA(lv_days) = is_container-days - c_included_days. + IF lv_days > 0. + rv_fee = day_rate( is_container-size ) * lv_days. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_size. + WHEN c_size_small. + rv_price = c_base_small. + WHEN c_size_medium. + rv_price = c_base_medium. + WHEN c_size_large. + rv_price = c_base_large. + WHEN OTHERS. + rv_price = 0. + ENDCASE. + ENDMETHOD. + + METHOD day_rate. + CASE iv_size. + WHEN c_size_small. + rv_rate = c_rate_small. + WHEN c_size_medium. + rv_rate = c_rate_medium. + WHEN c_size_large. + rv_rate = c_rate_large. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD hazard_fee. + CASE iv_size. + WHEN c_size_small. + rv_fee = c_hazard_small. + WHEN c_size_medium. + rv_fee = c_hazard_medium. + WHEN c_size_large. + rv_fee = c_hazard_large. + WHEN OTHERS. + rv_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD bulk_discount. + IF iv_count >= c_min_containers. + rv_percent = c_percent_bulk. + ELSE. + rv_percent = 0. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/faulty/m2_skip_hire_calc.clas.abap b/tasks_gen/train/G1030/faulty/m2_skip_hire_calc.clas.abap new file mode 100644 index 0000000..7d8850d --- /dev/null +++ b/tasks_gen/train/G1030/faulty/m2_skip_hire_calc.clas.abap @@ -0,0 +1,174 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + + PRIVATE SECTION. + + CONSTANTS c_size_small TYPE ty_size VALUE 'SMALL'. + CONSTANTS c_size_medium TYPE ty_size VALUE 'MEDIUM'. + CONSTANTS c_size_large TYPE ty_size VALUE 'LARGE'. + CONSTANTS c_base_small TYPE ty_amount VALUE '90.00'. + CONSTANTS c_base_medium TYPE ty_amount VALUE '140.00'. + CONSTANTS c_base_large TYPE ty_amount VALUE '200.00'. + CONSTANTS c_rate_small TYPE ty_amount VALUE '8.00'. + CONSTANTS c_rate_medium TYPE ty_amount VALUE '12.00'. + CONSTANTS c_rate_large TYPE ty_amount VALUE '16.00'. + CONSTANTS c_hazard_small TYPE ty_amount VALUE '60.00'. + CONSTANTS c_hazard_medium TYPE ty_amount VALUE '80.00'. + CONSTANTS c_hazard_large TYPE ty_amount VALUE '100.00'. + CONSTANTS c_permit_fee TYPE ty_amount VALUE '45.00'. + CONSTANTS c_min_charge TYPE ty_amount VALUE '120.00'. + CONSTANTS c_included_days TYPE i VALUE 7. + CONSTANTS c_min_days TYPE i VALUE 1. + CONSTANTS c_min_containers TYPE i VALUE 5. + CONSTANTS c_percent_bulk TYPE i VALUE 10. + CONSTANTS c_percent_base TYPE i VALUE 100. + + METHODS base_price + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_price) TYPE ty_amount. + + METHODS day_rate + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS hazard_fee + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS extra_days_fee + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS net_charge + IMPORTING iv_net TYPE ty_amount + iv_count TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + DATA lv_net TYPE ty_amount. + DATA lv_count TYPE i. + DATA lv_line TYPE ty_amount. + + LOOP AT it_containers INTO DATA(ls_container). + lv_line = container_charge( ls_container ). + IF lv_line > 0. + lv_net = lv_net + lv_line. + lv_count = lv_count + 1. + ENDIF. + ENDLOOP. + + rv_charge = net_charge( iv_net = lv_net iv_count = lv_count ). + ENDMETHOD. + + METHOD container_charge. + IF is_container-days < c_min_days. + RETURN. + ENDIF. + DATA(lv_base) = base_price( is_container-size ). + IF lv_base = 0. + RETURN. + ENDIF. + rv_charge = lv_base + extra_days_fee( is_container ). + IF is_container-hazardous = abap_true. + rv_charge = rv_charge + hazard_fee( is_container-size ). + ENDIF. + IF is_container-permit = abap_true. + rv_charge = rv_charge + c_permit_fee. + ENDIF. + ENDMETHOD. + + METHOD net_charge. + IF iv_net <> 0. + RETURN. + ENDIF. + rv_charge = round( val = iv_net * ( c_percent_base - bulk_discount( iv_count ) ) + / c_percent_base dec = 2 ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD extra_days_fee. + DATA(lv_days) = is_container-days - c_included_days. + IF lv_days > 0. + rv_fee = day_rate( is_container-size ) * lv_days. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_size. + WHEN c_size_small. + rv_price = c_base_small. + WHEN c_size_medium. + rv_price = c_base_medium. + WHEN c_size_large. + rv_price = c_base_large. + WHEN OTHERS. + rv_price = 0. + ENDCASE. + ENDMETHOD. + + METHOD day_rate. + CASE iv_size. + WHEN c_size_small. + rv_rate = c_rate_small. + WHEN c_size_medium. + rv_rate = c_rate_medium. + WHEN c_size_large. + rv_rate = c_rate_large. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD hazard_fee. + CASE iv_size. + WHEN c_size_small. + rv_fee = c_hazard_small. + WHEN c_size_medium. + rv_fee = c_hazard_medium. + WHEN c_size_large. + rv_fee = c_hazard_large. + WHEN OTHERS. + rv_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD bulk_discount. + IF iv_count >= c_min_containers. + rv_percent = c_percent_bulk. + ELSE. + rv_percent = 0. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/faulty/m4_skip_hire_calc.clas.abap b/tasks_gen/train/G1030/faulty/m4_skip_hire_calc.clas.abap new file mode 100644 index 0000000..3f3591a --- /dev/null +++ b/tasks_gen/train/G1030/faulty/m4_skip_hire_calc.clas.abap @@ -0,0 +1,174 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + + PRIVATE SECTION. + + CONSTANTS c_size_small TYPE ty_size VALUE 'SMALL'. + CONSTANTS c_size_medium TYPE ty_size VALUE 'MEDIUM'. + CONSTANTS c_size_large TYPE ty_size VALUE 'LARGE'. + CONSTANTS c_base_small TYPE ty_amount VALUE '90.00'. + CONSTANTS c_base_medium TYPE ty_amount VALUE '140.00'. + CONSTANTS c_base_large TYPE ty_amount VALUE '200.00'. + CONSTANTS c_rate_small TYPE ty_amount VALUE '8.00'. + CONSTANTS c_rate_medium TYPE ty_amount VALUE '12.00'. + CONSTANTS c_rate_large TYPE ty_amount VALUE '16.00'. + CONSTANTS c_hazard_small TYPE ty_amount VALUE '60.00'. + CONSTANTS c_hazard_medium TYPE ty_amount VALUE '80.00'. + CONSTANTS c_hazard_large TYPE ty_amount VALUE '100.00'. + CONSTANTS c_permit_fee TYPE ty_amount VALUE '45.00'. + CONSTANTS c_min_charge TYPE ty_amount VALUE '120.00'. + CONSTANTS c_included_days TYPE i VALUE 7. + CONSTANTS c_min_days TYPE i VALUE 1. + CONSTANTS c_min_containers TYPE i VALUE 5. + CONSTANTS c_percent_bulk TYPE i VALUE 10. + CONSTANTS c_percent_base TYPE i VALUE 100. + + METHODS base_price + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_price) TYPE ty_amount. + + METHODS day_rate + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS hazard_fee + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS extra_days_fee + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS net_charge + IMPORTING iv_net TYPE ty_amount + iv_count TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + DATA lv_net TYPE ty_amount. + DATA lv_count TYPE i. + DATA lv_line TYPE ty_amount. + + LOOP AT it_containers INTO DATA(ls_container). + lv_line = container_charge( ls_container ). + IF lv_line > 0. + lv_net = lv_net + lv_line. + lv_count = lv_count + 1. + ENDIF. + ENDLOOP. + + rv_charge = net_charge( iv_net = lv_net iv_count = lv_count ). + ENDMETHOD. + + METHOD container_charge. + IF is_container-days < c_min_days. + RETURN. + ENDIF. + DATA(lv_base) = base_price( is_container-size ). + IF lv_base = 0. + RETURN. + ENDIF. + rv_charge = lv_base + extra_days_fee( is_container ). + IF is_container-hazardous = abap_true. + rv_charge = rv_charge + hazard_fee( is_container-size ). + ENDIF. + IF is_container-permit = abap_true. + rv_charge = rv_charge + c_permit_fee. + ENDIF. + ENDMETHOD. + + METHOD net_charge. + IF iv_net = 0. + RETURN. + ENDIF. + rv_charge = round( val = iv_net * ( c_percent_base - bulk_discount( iv_count ) ) + / c_percent_base dec = 2 ). + IF rv_charge >= c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD extra_days_fee. + DATA(lv_days) = is_container-days - c_included_days. + IF lv_days > 0. + rv_fee = day_rate( is_container-size ) * lv_days. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_size. + WHEN c_size_small. + rv_price = c_base_small. + WHEN c_size_medium. + rv_price = c_base_medium. + WHEN c_size_large. + rv_price = c_base_large. + WHEN OTHERS. + rv_price = 0. + ENDCASE. + ENDMETHOD. + + METHOD day_rate. + CASE iv_size. + WHEN c_size_small. + rv_rate = c_rate_small. + WHEN c_size_medium. + rv_rate = c_rate_medium. + WHEN c_size_large. + rv_rate = c_rate_large. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD hazard_fee. + CASE iv_size. + WHEN c_size_small. + rv_fee = c_hazard_small. + WHEN c_size_medium. + rv_fee = c_hazard_medium. + WHEN c_size_large. + rv_fee = c_hazard_large. + WHEN OTHERS. + rv_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD bulk_discount. + IF iv_count >= c_min_containers. + rv_percent = c_percent_bulk. + ELSE. + rv_percent = 0. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/generation.json b/tasks_gen/train/G1030/generation.json new file mode 100644 index 0000000..325de05 --- /dev/null +++ b/tasks_gen/train/G1030/generation.json @@ -0,0 +1,36 @@ +{ + "id": "G1030", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tailor_pricer.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1002 (similarity spec 0.69, rules 0.62, names 0.33). Choose a different business topic and different object names.", + "Too close to task G1010 (similarity spec 0.57, rules 0.64, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 65.8, + "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/G1030/hidden/t61_hidden.clas.abap b/tasks_gen/train/G1030/hidden/t61_hidden.clas.abap new file mode 100644 index 0000000..fbd281c --- /dev/null +++ b/tasks_gen/train/G1030/hidden/t61_hidden.clas.abap @@ -0,0 +1,155 @@ +CLASS {{p}}t61_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}skip_hire_calc. + METHODS setup. + METHODS empty_order_is_zero FOR TESTING. + METHODS small_order_minimum FOR TESTING. + METHODS base_prices_by_size FOR TESTING. + METHODS seven_days_included FOR TESTING. + METHODS extra_days_charged FOR TESTING. + METHODS hazardous_surcharge FOR TESTING. + METHODS permit_fee FOR TESTING. + METHODS invalid_containers_ignored FOR TESTING. + METHODS bulk_discount_five FOR TESTING. + METHODS discount_needs_five_valid FOR TESTING. + METHODS container_charge_direct FOR TESTING. + METHODS discount_boundaries FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t61_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}skip_hire_calc( ). + ENDMETHOD. + + METHOD empty_order_is_zero. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '0.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD small_order_minimum. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '120.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD base_prices_by_size. + DATA(lt_medium) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) ). + DATA(lt_large) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'LARGE' days = 1 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '140.00' ) + act = mo_cut->hire_charge( lt_medium ) msg = `MEDIUM` ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '200.00' ) + act = mo_cut->hire_charge( lt_large ) msg = `LARGE` ). + ENDMETHOD. + + METHOD seven_days_included. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'MEDIUM' days = 7 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '140.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD extra_days_charged. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'MEDIUM' days = 10 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '176.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD hazardous_surcharge. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_true permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '150.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD permit_fee. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '135.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD invalid_containers_ignored. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'HUGE' days = 3 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 0 hazardous = abap_false permit = abap_false ) + ( size = 'LARGE' days = -2 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '0.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD bulk_discount_five. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '405.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD discount_needs_five_valid. + DATA(lt_four) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) ). + DATA(lt_with_invalid) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'HUGE' days = 5 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '360.00' ) + act = mo_cut->hire_charge( lt_four ) msg = `four containers` ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '360.00' ) + act = mo_cut->hire_charge( lt_with_invalid ) msg = `invalid row not counted` ). + ENDMETHOD. + + METHOD container_charge_direct. + DATA(ls_small) = VALUE {{p}}skip_hire_calc=>ty_container( + size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ). + DATA(ls_large) = VALUE {{p}}skip_hire_calc=>ty_container( + size = 'LARGE' days = 10 hazardous = abap_false permit = abap_false ). + DATA(ls_no_days) = VALUE {{p}}skip_hire_calc=>ty_container( + size = 'MEDIUM' days = 0 hazardous = abap_false permit = abap_false ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '90.00' ) + act = mo_cut->container_charge( ls_small ) msg = `SMALL one day` ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '248.00' ) + act = mo_cut->container_charge( ls_large ) msg = `LARGE ten days` ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '0.00' ) + act = mo_cut->container_charge( ls_no_days ) msg = `no days` ). + ENDMETHOD. + + METHOD discount_boundaries. + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->bulk_discount( 0 ) msg = `none` ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->bulk_discount( 4 ) msg = `four` ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = mo_cut->bulk_discount( 5 ) msg = `five` ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = mo_cut->bulk_discount( 9 ) msg = `nine` ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/mutation.json b/tasks_gen/train/G1030/mutation.json new file mode 100644 index 0000000..5c940fe --- /dev/null +++ b/tasks_gen/train/G1030/mutation.json @@ -0,0 +1,76 @@ +{ + "task": "G1030", + "mutants": [ + { + "object": "{{P}}SKIP_HIRE_CALC", + "mutant": "line 85: + -> - (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "BULK_DISCOUNT_FIVE" + ] + }, + { + "object": "{{P}}SKIP_HIRE_CALC", + "mutant": "line 104: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "BASE_PRICES_BY_SIZE", + "BULK_DISCOUNT_FIVE", + "CONTAINER_CHARGE_DIRECT", + "DISCOUNT_NEEDS_FIVE_VALID", + "EXTRA_DAYS_CHARGED", + "HAZARDOUS_SURCHARGE", + "PERMIT_FEE", + "SEVEN_DAYS_INCLUDED", + "SMALL_ORDER_MINIMUM" + ] + }, + { + "object": "{{P}}SKIP_HIRE_CALC", + "mutant": "line 110: = -> <> (eq)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "BASE_PRICES_BY_SIZE", + "BULK_DISCOUNT_FIVE", + "DISCOUNT_NEEDS_FIVE_VALID", + "EMPTY_ORDER_IS_ZERO", + "EXTRA_DAYS_CHARGED", + "HAZARDOUS_SURCHARGE", + "INVALID_CONTAINERS_IGNORED", + "PERMIT_FEE", + "SEVEN_DAYS_INCLUDED", + "SMALL_ORDER_MINIMUM" + ] + }, + { + "object": "{{P}}SKIP_HIRE_CALC", + "mutant": "line 114: 2 -> 3 (const)", + "status": "survived", + "hidden": "12/12", + "failed_tests": [] + }, + { + "object": "{{P}}SKIP_HIRE_CALC", + "mutant": "line 115: < -> >= (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "BASE_PRICES_BY_SIZE", + "BULK_DISCOUNT_FIVE", + "DISCOUNT_NEEDS_FIVE_VALID", + "EXTRA_DAYS_CHARGED", + "HAZARDOUS_SURCHARGE", + "PERMIT_FEE", + "SEVEN_DAYS_INCLUDED", + "SMALL_ORDER_MINIMUM" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1030/reference/skip_hire_calc.clas.abap b/tasks_gen/train/G1030/reference/skip_hire_calc.clas.abap new file mode 100644 index 0000000..e06b72c --- /dev/null +++ b/tasks_gen/train/G1030/reference/skip_hire_calc.clas.abap @@ -0,0 +1,174 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + + PRIVATE SECTION. + + CONSTANTS c_size_small TYPE ty_size VALUE 'SMALL'. + CONSTANTS c_size_medium TYPE ty_size VALUE 'MEDIUM'. + CONSTANTS c_size_large TYPE ty_size VALUE 'LARGE'. + CONSTANTS c_base_small TYPE ty_amount VALUE '90.00'. + CONSTANTS c_base_medium TYPE ty_amount VALUE '140.00'. + CONSTANTS c_base_large TYPE ty_amount VALUE '200.00'. + CONSTANTS c_rate_small TYPE ty_amount VALUE '8.00'. + CONSTANTS c_rate_medium TYPE ty_amount VALUE '12.00'. + CONSTANTS c_rate_large TYPE ty_amount VALUE '16.00'. + CONSTANTS c_hazard_small TYPE ty_amount VALUE '60.00'. + CONSTANTS c_hazard_medium TYPE ty_amount VALUE '80.00'. + CONSTANTS c_hazard_large TYPE ty_amount VALUE '100.00'. + CONSTANTS c_permit_fee TYPE ty_amount VALUE '45.00'. + CONSTANTS c_min_charge TYPE ty_amount VALUE '120.00'. + CONSTANTS c_included_days TYPE i VALUE 7. + CONSTANTS c_min_days TYPE i VALUE 1. + CONSTANTS c_min_containers TYPE i VALUE 5. + CONSTANTS c_percent_bulk TYPE i VALUE 10. + CONSTANTS c_percent_base TYPE i VALUE 100. + + METHODS base_price + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_price) TYPE ty_amount. + + METHODS day_rate + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS hazard_fee + IMPORTING iv_size TYPE ty_size + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS extra_days_fee + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_fee) TYPE ty_amount. + + METHODS net_charge + IMPORTING iv_net TYPE ty_amount + iv_count TYPE i + RETURNING VALUE(rv_charge) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + DATA lv_net TYPE ty_amount. + DATA lv_count TYPE i. + DATA lv_line TYPE ty_amount. + + LOOP AT it_containers INTO DATA(ls_container). + lv_line = container_charge( ls_container ). + IF lv_line > 0. + lv_net = lv_net + lv_line. + lv_count = lv_count + 1. + ENDIF. + ENDLOOP. + + rv_charge = net_charge( iv_net = lv_net iv_count = lv_count ). + ENDMETHOD. + + METHOD container_charge. + IF is_container-days < c_min_days. + RETURN. + ENDIF. + DATA(lv_base) = base_price( is_container-size ). + IF lv_base = 0. + RETURN. + ENDIF. + rv_charge = lv_base + extra_days_fee( is_container ). + IF is_container-hazardous = abap_true. + rv_charge = rv_charge + hazard_fee( is_container-size ). + ENDIF. + IF is_container-permit = abap_true. + rv_charge = rv_charge + c_permit_fee. + ENDIF. + ENDMETHOD. + + METHOD net_charge. + IF iv_net = 0. + RETURN. + ENDIF. + rv_charge = round( val = iv_net * ( c_percent_base - bulk_discount( iv_count ) ) + / c_percent_base dec = 2 ). + IF rv_charge < c_min_charge. + rv_charge = c_min_charge. + ENDIF. + ENDMETHOD. + + METHOD extra_days_fee. + DATA(lv_days) = is_container-days - c_included_days. + IF lv_days > 0. + rv_fee = day_rate( is_container-size ) * lv_days. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_size. + WHEN c_size_small. + rv_price = c_base_small. + WHEN c_size_medium. + rv_price = c_base_medium. + WHEN c_size_large. + rv_price = c_base_large. + WHEN OTHERS. + rv_price = 0. + ENDCASE. + ENDMETHOD. + + METHOD day_rate. + CASE iv_size. + WHEN c_size_small. + rv_rate = c_rate_small. + WHEN c_size_medium. + rv_rate = c_rate_medium. + WHEN c_size_large. + rv_rate = c_rate_large. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD hazard_fee. + CASE iv_size. + WHEN c_size_small. + rv_fee = c_hazard_small. + WHEN c_size_medium. + rv_fee = c_hazard_medium. + WHEN c_size_large. + rv_fee = c_hazard_large. + WHEN OTHERS. + rv_fee = 0. + ENDCASE. + ENDMETHOD. + + METHOD bulk_discount. + IF iv_count >= c_min_containers. + rv_percent = c_percent_bulk. + ELSE. + rv_percent = 0. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/reference/skip_hire_calc.testclasses.abap b/tasks_gen/train/G1030/reference/skip_hire_calc.testclasses.abap new file mode 100644 index 0000000..ba1da64 --- /dev/null +++ b/tasks_gen/train/G1030/reference/skip_hire_calc.testclasses.abap @@ -0,0 +1,65 @@ +CLASS ltc_skip_hire_calc DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}skip_hire_calc. + METHODS setup. + METHODS minimum_charge_applies FOR TESTING. + METHODS extra_days_are_charged FOR TESTING. + METHODS hazardous_and_permit FOR TESTING. + METHODS bulk_discount FOR TESTING. + METHODS invalid_rows_are_free FOR TESTING. + +ENDCLASS. + + +CLASS ltc_skip_hire_calc IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}skip_hire_calc( ). + ENDMETHOD. + + METHOD minimum_charge_applies. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '120.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD extra_days_are_charged. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'MEDIUM' days = 10 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '176.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD hazardous_and_permit. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'SMALL' days = 1 hazardous = abap_true permit = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '195.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD bulk_discount. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) + ( size = 'MEDIUM' days = 1 hazardous = abap_false permit = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '630.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + + METHOD invalid_rows_are_free. + DATA(lt_containers) = VALUE {{p}}skip_hire_calc=>tt_containers( + ( size = 'HUGE' days = 3 hazardous = abap_true permit = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}skip_hire_calc=>ty_amount( '0.00' ) + act = mo_cut->hire_charge( lt_containers ) ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/seed/skip_hire_calc.clas.abap b/tasks_gen/train/G1030/seed/skip_hire_calc.clas.abap new file mode 100644 index 0000000..37b7ef4 --- /dev/null +++ b/tasks_gen/train/G1030/seed/skip_hire_calc.clas.abap @@ -0,0 +1,169 @@ +CLASS {{p}}skip_hire_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_size TYPE c LENGTH 6, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + BEGIN OF ty_container, + size TYPE ty_size, + days TYPE i, + hazardous TYPE abap_bool, + permit TYPE abap_bool, + END OF ty_container, + tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + + METHODS hire_charge + IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS container_charge + IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + + METHODS bulk_discount + IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. + +ENDCLASS. + + +CLASS {{p}}skip_hire_calc IMPLEMENTATION. + + METHOD hire_charge. + + DATA: lv_net TYPE ty_amount, + lv_pct TYPE i, + lv_count TYPE i, + lv_base TYPE ty_amount, + lv_rate TYPE ty_amount, + lv_extra TYPE ty_amount, + lv_days TYPE i, + ls_container TYPE ty_container. + + CLEAR rv_charge. + + LOOP AT it_containers INTO ls_container. + + CLEAR lv_base. + CLEAR lv_rate. + + IF ls_container-size = 'SMALL'. + lv_base = '90.00'. + lv_rate = '8.00'. + ELSEIF ls_container-size = 'MEDIUM'. + lv_base = '140.00'. + lv_rate = '12.00'. + ELSEIF ls_container-size = 'LARGE'. + lv_base = '200.00'. + lv_rate = '16.00'. + ENDIF. + + IF lv_base > 0 AND ls_container-days >= 1. + + CLEAR lv_extra. + lv_days = ls_container-days - 7. + IF lv_days > 0. + lv_extra = lv_rate * lv_days. + ENDIF. + + lv_net = lv_net + lv_base + lv_extra. + lv_count = lv_count + 1. + + IF ls_container-hazardous = abap_true. + IF ls_container-size = 'SMALL'. + lv_net = lv_net + '60.00'. + ELSEIF ls_container-size = 'MEDIUM'. + lv_net = lv_net + '80.00'. + ELSE. + lv_net = lv_net + '100.00'. + ENDIF. + ENDIF. + + IF ls_container-permit = abap_true. + lv_net = lv_net + '45.00'. + ENDIF. + + ENDIF. + + ENDLOOP. + + IF lv_net = 0. + RETURN. + ENDIF. + + IF lv_count >= 5. + lv_pct = 10. + ELSE. + lv_pct = 0. + ENDIF. + + rv_charge = lv_net * ( 100 - lv_pct ) / 100. + rv_charge = round( val = rv_charge dec = 2 ). + + IF rv_charge < 120. + rv_charge = 120. + ENDIF. + + ENDMETHOD. + + METHOD container_charge. + + DATA: lv_base TYPE ty_amount, + lv_rate TYPE ty_amount, + lv_extra TYPE ty_amount, + lv_days TYPE i. + + CLEAR rv_charge. + + IF is_container-size = 'SMALL'. + lv_base = '90.00'. + lv_rate = '8.00'. + ELSEIF is_container-size = 'MEDIUM'. + lv_base = '140.00'. + lv_rate = '12.00'. + ELSEIF is_container-size = 'LARGE'. + lv_base = '200.00'. + lv_rate = '16.00'. + ELSE. + RETURN. + ENDIF. + + IF is_container-days < 1. + RETURN. + ENDIF. + + lv_days = is_container-days - 7. + IF lv_days > 0. + lv_extra = lv_rate * lv_days. + ENDIF. + + rv_charge = lv_base + lv_extra. + + IF is_container-hazardous = abap_true. + IF is_container-size = 'SMALL'. + rv_charge = rv_charge + '60.00'. + ELSEIF is_container-size = 'MEDIUM'. + rv_charge = rv_charge + '80.00'. + ELSE. + rv_charge = rv_charge + '100.00'. + ENDIF. + ENDIF. + + IF is_container-permit = abap_true. + rv_charge = rv_charge + '45.00'. + ENDIF. + + ENDMETHOD. + + METHOD bulk_discount. + + IF iv_count >= 5. + rv_percent = 10. + ELSE. + rv_percent = 0. + ENDIF. + + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1030/spec.md b/tasks_gen/train/G1030/spec.md new file mode 100644 index 0000000..19689ad --- /dev/null +++ b/tasks_gen/train/G1030/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A waste container hire company rents skips to its customers. The depot terminal +calls the class {{P}}SKIP_HIRE_CALC for each hire order. The class is legacy +code. Refactor the class. The behavior must stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SKIP_HIRE_CALC exists in package $TMP. It is active. +- The class is public and final. It has a public constructor without parameters. +- The legacy implementation is hard to read. It has a long method, deep nesting, + duplicated logic, and unnamed numbers. +- The depot terminal calls the class. The result of a call must not change. + +# 4. Contract +- Refactor the class {{P}}SKIP_HIRE_CALC in package $TMP. The name of the class + stays the same. The class stays public and final and keeps a public + constructor without parameters. +- Keep the public interface. Do not change a name and do not change a type: + - TYPES ty_size TYPE c LENGTH 6. + - TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + - TYPES BEGIN OF ty_container. Components: size TYPE ty_size, days TYPE i, + hazardous TYPE abap_bool, permit TYPE abap_bool. END OF ty_container. + - TYPES tt_containers TYPE STANDARD TABLE OF ty_container WITH DEFAULT KEY. + - METHOD hire_charge IMPORTING it_containers TYPE tt_containers + RETURNING VALUE(rv_charge) TYPE ty_amount. + - METHOD container_charge IMPORTING is_container TYPE ty_container + RETURNING VALUE(rv_charge) TYPE ty_amount. + - METHOD bulk_discount IMPORTING iv_count TYPE i + RETURNING VALUE(rv_percent) TYPE i. +- Do not add other public methods. + +# 5. Business rules +- The behavior of the class must stay the same for every input. +- The legacy implementation in the seed defines the behavior. Read the legacy + code. The legacy code contains all the rules of the calculation. +- Do not change the result of a call. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Methods below 40 statements. No deep nesting. + No duplicated logic. Replace each unnamed number with a named constant. Use + modern ABAP syntax. No global variable. No comment that restates the code. +- Out of scope: do not create new objects. Do not change other objects. + +# 7. Acceptance +- The class {{P}}SKIP_HIRE_CALC 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/G1030/task.json b/tasks_gen/train/G1030/task.json new file mode 100644 index 0000000..3c5f7dd --- /dev/null +++ b/tasks_gen/train/G1030/task.json @@ -0,0 +1,53 @@ +{ + "id": "G1030", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}SKIP_HIRE_CALC", + "file": "seed/skip_hire_calc.clas.abap", + "description": "Legacy waste container hire calculator: long nested method, duplicated per-container logic, unnamed numbers" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SKIP_HIRE_CALC" + } + ], + "out_of_scope": [ + "Create new objects", + "Change objects other than {{P}}SKIP_HIRE_CALC" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T61_HIDDEN", + "file": "hidden/t61_hidden.clas.abap", + "description": "T61 hidden tests for the unchanged behavior of the waste container hire calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SKIP_HIRE_CALC", + "file": "reference/skip_hire_calc.clas.abap", + "description": "Refactored waste container hire calculator", + "testclasses_file": "reference/skip_hire_calc.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "magic_numbers", + "nesting_depth", + "duplication" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1031/faulty/m0_i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/faulty/m0_i_film_revenue.ddls.asddls new file mode 100644 index 0000000..dd4d6f5 --- /dev/null +++ b/tasks_gen/train/G1031/faulty/m0_i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + left outer join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.film diff --git a/tasks_gen/train/G1031/faulty/m1_i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/faulty/m1_i_film_revenue.ddls.asddls new file mode 100644 index 0000000..d603de0 --- /dev/null +++ b/tasks_gen/train/G1031/faulty/m1_i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id <> s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.film diff --git a/tasks_gen/train/G1031/faulty/m2_i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/faulty/m2_i_film_revenue.ddls.asddls new file mode 100644 index 0000000..4968778 --- /dev/null +++ b/tasks_gen/train/G1031/faulty/m2_i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + max( t.price ) as revenue +} +where s.status = 'A' +group by + s.film diff --git a/tasks_gen/train/G1031/faulty/m3_i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/faulty/m3_i_film_revenue.ddls.asddls new file mode 100644 index 0000000..c77bd69 --- /dev/null +++ b/tasks_gen/train/G1031/faulty/m3_i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status <> 'A' +group by + s.film diff --git a/tasks_gen/train/G1031/faulty/m4_i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/faulty/m4_i_film_revenue.ddls.asddls new file mode 100644 index 0000000..37cf162 --- /dev/null +++ b/tasks_gen/train/G1031/faulty/m4_i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'Z' +group by + s.film diff --git a/tasks_gen/train/G1031/generation.json b/tasks_gen/train/G1031/generation.json new file mode 100644 index 0000000..f7cd2bb --- /dev/null +++ b/tasks_gen/train/G1031/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1031", + "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/G1031/hidden/t_cinema_hidden.clas.abap b/tasks_gen/train/G1031/hidden/t_cinema_hidden.clas.abap new file mode 100644 index 0000000..29c21de --- /dev/null +++ b/tasks_gen/train/G1031/hidden/t_cinema_hidden.clas.abap @@ -0,0 +1,76 @@ +CLASS {{p}}t_cinema_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_film FOR TESTING. + METHODS ticket_count FOR TESTING. + METHODS revenue FOR TESTING. + METHODS cancelled_excluded FOR TESTING. + METHODS different_films FOR TESTING. + METHODS no_tickets FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_cinema_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_FILM_REVENUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_screen TYPE STANDARD TABLE OF {{p}}screen WITH EMPTY KEY. + DATA lt_ticket TYPE STANDARD TABLE OF {{p}}ticket WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_screen = VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ( show_id = 'S3' film = 'F2' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S4' film = 'F3' show_date = '20250103' hall = 'H1' status = 'C' ) + ( show_id = 'S5' film = 'F4' show_date = '20250104' hall = 'H1' status = 'A' ) + ). + lt_ticket = VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S1' ticket_no = 2 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ( show_id = 'S3' ticket_no = 1 price = '8.00' ) + ( show_id = 'S4' ticket_no = 1 price = '9.00' ) + ). + go_env->insert_test_data( lt_screen ). + go_env->insert_test_data( lt_ticket ). + ENDMETHOD. + + METHOD one_row_per_film. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD ticket_count. + SELECT SINGLE ticket_count FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD revenue. + SELECT SINGLE revenue FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '32.50' ) act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. + + METHOD cancelled_excluded. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD different_films. + SELECT COUNT(*) FROM {{p}}i_film_revenue INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD no_tickets. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1031/mutation.json b/tasks_gen/train/G1031/mutation.json new file mode 100644 index 0000000..92066dc --- /dev/null +++ b/tasks_gen/train/G1031/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1031", + "mutants": [ + { + "object": "{{P}}I_FILM_REVENUE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "DIFFERENT_FILMS", + "NO_TICKETS" + ] + }, + { + "object": "{{P}}I_FILM_REVENUE", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "DIFFERENT_FILMS", + "NO_TICKETS", + "REVENUE", + "TICKET_COUNT" + ] + }, + { + "object": "{{P}}I_FILM_REVENUE", + "mutant": "line 9: sum( -> max( (agg)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "REVENUE" + ] + }, + { + "object": "{{P}}I_FILM_REVENUE", + "mutant": "line 11: = -> <> (eq)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "CANCELLED_EXCLUDED", + "DIFFERENT_FILMS", + "ONE_ROW_PER_FILM", + "REVENUE", + "TICKET_COUNT" + ] + }, + { + "object": "{{P}}I_FILM_REVENUE", + "mutant": "line 11: 'A' -> 'Z' (lit)", + "status": "killed", + "hidden": "2/6", + "failed_tests": [ + "DIFFERENT_FILMS", + "ONE_ROW_PER_FILM", + "REVENUE", + "TICKET_COUNT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1031/reference/i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/reference/i_film_revenue.ddls.asddls new file mode 100644 index 0000000..1045f29 --- /dev/null +++ b/tasks_gen/train/G1031/reference/i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.film diff --git a/tasks_gen/train/G1031/reference/t_cinema_test.clas.abap b/tasks_gen/train/G1031/reference/t_cinema_test.clas.abap new file mode 100644 index 0000000..0188aca --- /dev/null +++ b/tasks_gen/train/G1031/reference/t_cinema_test.clas.abap @@ -0,0 +1,46 @@ +CLASS {{p}}t_cinema_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_per_film FOR TESTING. + METHODS revenue FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_cinema_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_FILM_REVENUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_screen TYPE STANDARD TABLE OF {{p}}screen WITH EMPTY KEY. + DATA lt_ticket TYPE STANDARD TABLE OF {{p}}ticket WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_screen = VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ). + lt_ticket = VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ). + go_env->insert_test_data( lt_screen ). + go_env->insert_test_data( lt_ticket ). + ENDMETHOD. + + METHOD one_row_per_film. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD revenue. + SELECT SINGLE revenue FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '22.50' ) act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1031/seed/i_film_revenue.ddls.asddls b/tasks_gen/train/G1031/seed/i_film_revenue.ddls.asddls new file mode 100644 index 0000000..756eb64 --- /dev/null +++ b/tasks_gen/train/G1031/seed/i_film_revenue.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.show_id as show_id, + s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.show_id, + s.film diff --git a/tasks_gen/train/G1031/seed/screen.tabl.asabap b/tasks_gen/train/G1031/seed/screen.tabl.asabap new file mode 100644 index 0000000..1118ca2 --- /dev/null +++ b/tasks_gen/train/G1031/seed/screen.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Screening' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}screen { + key client : abap.clnt not null; + key show_id : abap.char(10) not null; + film : abap.char(10); + show_date : abap.dats; + hall : abap.char(4); + status : abap.char(1); +} diff --git a/tasks_gen/train/G1031/seed/seed_cinema.clas.abap b/tasks_gen/train/G1031/seed/seed_cinema.clas.abap new file mode 100644 index 0000000..f70b258 --- /dev/null +++ b/tasks_gen/train/G1031/seed/seed_cinema.clas.abap @@ -0,0 +1,28 @@ +CLASS {{p}}seed_cinema DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS {{p}}seed_cinema IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}screen. + DELETE FROM {{p}}ticket. + INSERT {{p}}screen FROM TABLE @( VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ( show_id = 'S3' film = 'F2' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S4' film = 'F3' show_date = '20250103' hall = 'H1' status = 'C' ) + ( show_id = 'S5' film = 'F4' show_date = '20250104' hall = 'H1' status = 'A' ) + ) ). + INSERT {{p}}ticket FROM TABLE @( VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S1' ticket_no = 2 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ( show_id = 'S3' ticket_no = 1 price = '8.00' ) + ( show_id = 'S4' ticket_no = 1 price = '9.00' ) + ) ). + out->write( 'Seed data inserted.' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1031/seed/ticket.tabl.asabap b/tasks_gen/train/G1031/seed/ticket.tabl.asabap new file mode 100644 index 0000000..89fd489 --- /dev/null +++ b/tasks_gen/train/G1031/seed/ticket.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Ticket' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}ticket { + key client : abap.clnt not null; + key show_id : abap.char(10) not null; + key ticket_no : abap.int4 not null; + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1031/spec.md b/tasks_gen/train/G1031/spec.md new file mode 100644 index 0000000..e58a02e --- /dev/null +++ b/tasks_gen/train/G1031/spec.md @@ -0,0 +1,38 @@ +# 1. Goal +The CDS view entity {{P}}I_FILM_REVENUE shows the revenue of each film in a +cinema chain. Users report that a film appears several times in the view, once +for each screening. Fix the view so that it shows one row for each film. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}SCREEN (screening), package $TMP. Key: SHOW_ID (CHAR 10). + Fields: FILM (CHAR 10), SHOW_DATE (DATS), HALL (CHAR 4), STATUS (CHAR 1). +- Table {{P}}TICKET (ticket), package $TMP. Key: SHOW_ID (CHAR 10), + TICKET_NO (INT4). Fields: PRICE (DEC 15,2, price of one ticket). +- The CDS view entity {{P}}I_FILM_REVENUE is in package $TMP. It joins + {{P}}SCREEN and {{P}}TICKET. + +# 4. Contract +- The CDS view entity {{P}}I_FILM_REVENUE stays in package $TMP. +- Elements, with these names: FILM (key), TICKET_COUNT, REVENUE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each film that has at least one ticket. Do not show a film + without tickets. +2. TICKET_COUNT is the number of tickets sold for the film. +3. REVENUE is the sum of the prices of all tickets of the film. +4. All other behavior of the view stays the same. + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Out of scope: do not change {{P}}SCREEN, {{P}}TICKET and {{P}}SEED_CINEMA. +- Coding standards: Clean ABAP. No ATC priority 1 or 2 findings. +- Out of scope: do not create other objects. + +# 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/G1031/task.json b/tasks_gen/train/G1031/task.json new file mode 100644 index 0000000..3b0e0e3 --- /dev/null +++ b/tasks_gen/train/G1031/task.json @@ -0,0 +1,78 @@ +{ + "id": "G1031", + "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}}SCREEN", + "file": "seed/screen.tabl.asabap", + "description": "Screening" + }, + { + "type": "TABL", + "name": "{{P}}TICKET", + "file": "seed/ticket.tabl.asabap", + "description": "Ticket" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_CINEMA", + "file": "seed/seed_cinema.clas.abap", + "description": "Seed data", + "run": true + }, + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "file": "seed/i_film_revenue.ddls.asddls", + "description": "Faulty view" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "fields": [ + "FILM", + "TICKET_COUNT", + "REVENUE" + ] + } + ], + "out_of_scope": [ + "{{P}}SCREEN", + "{{P}}TICKET", + "{{P}}SEED_CINEMA" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_CINEMA_HIDDEN", + "file": "hidden/t_cinema_hidden.clas.abap", + "description": "Hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "file": "reference/i_film_revenue.ddls.asddls", + "description": "Fixed view" + }, + { + "type": "CLAS", + "name": "{{P}}T_CINEMA_TEST", + "file": "reference/t_cinema_test.clas.abap", + "description": "Own tests" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1032/faulty/m1_i_tutor_load.ddls.asddls b/tasks_gen/train/G1032/faulty/m1_i_tutor_load.ddls.asddls new file mode 100644 index 0000000..82e7c1e --- /dev/null +++ b/tasks_gen/train/G1032/faulty/m1_i_tutor_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tutor load' +define view entity {{P}}I_TUTOR_LOAD + as select from {{p}}tutor as tutor + inner join {{p}}lesson as lesson on lesson.tutor_id <> tutor.tutor_id +{ + key tutor.tutor_id as tutor_id, + tutor.tutor_name as tutor_name, + count( * ) as lesson_count, + sum( lesson.dur_min ) as total_minutes +} +where lesson.dur_min > 0 +group by + tutor.tutor_id, + tutor.tutor_name +having count( * ) >= 3 diff --git a/tasks_gen/train/G1032/faulty/m2_i_tutor_load.ddls.asddls b/tasks_gen/train/G1032/faulty/m2_i_tutor_load.ddls.asddls new file mode 100644 index 0000000..e7f4be6 --- /dev/null +++ b/tasks_gen/train/G1032/faulty/m2_i_tutor_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tutor load' +define view entity {{P}}I_TUTOR_LOAD + as select from {{p}}tutor as tutor + inner join {{p}}lesson as lesson on lesson.tutor_id = tutor.tutor_id +{ + key tutor.tutor_id as tutor_id, + tutor.tutor_name as tutor_name, + count( * ) as lesson_count, + max( lesson.dur_min ) as total_minutes +} +where lesson.dur_min > 0 +group by + tutor.tutor_id, + tutor.tutor_name +having count( * ) >= 3 diff --git a/tasks_gen/train/G1032/faulty/m3_i_tutor_load.ddls.asddls b/tasks_gen/train/G1032/faulty/m3_i_tutor_load.ddls.asddls new file mode 100644 index 0000000..108c878 --- /dev/null +++ b/tasks_gen/train/G1032/faulty/m3_i_tutor_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tutor load' +define view entity {{P}}I_TUTOR_LOAD + as select from {{p}}tutor as tutor + inner join {{p}}lesson as lesson on lesson.tutor_id = tutor.tutor_id +{ + key tutor.tutor_id as tutor_id, + tutor.tutor_name as tutor_name, + count( * ) as lesson_count, + sum( lesson.dur_min ) as total_minutes +} +where lesson.dur_min <= 0 +group by + tutor.tutor_id, + tutor.tutor_name +having count( * ) >= 3 diff --git a/tasks_gen/train/G1032/faulty/m4_i_tutor_load.ddls.asddls b/tasks_gen/train/G1032/faulty/m4_i_tutor_load.ddls.asddls new file mode 100644 index 0000000..ccb966b --- /dev/null +++ b/tasks_gen/train/G1032/faulty/m4_i_tutor_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tutor load' +define view entity {{P}}I_TUTOR_LOAD + as select from {{p}}tutor as tutor + inner join {{p}}lesson as lesson on lesson.tutor_id = tutor.tutor_id +{ + key tutor.tutor_id as tutor_id, + tutor.tutor_name as tutor_name, + count( * ) as lesson_count, + sum( lesson.dur_min ) as total_minutes +} +where lesson.dur_min > 0 +group by + tutor.tutor_id, + tutor.tutor_name +having count( * ) >= 4 diff --git a/tasks_gen/train/G1032/generation.json b/tasks_gen/train/G1032/generation.json new file mode 100644 index 0000000..811f1e9 --- /dev/null +++ b/tasks_gen/train/G1032/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1032", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/lesson.tabl.asabap: field name minutes is a reserved word; choose another name" + ], + "by_check": { + "static": 1 + } + }, + { + "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/G1032/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1032/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..ef38f0e --- /dev/null +++ b/tasks_gen/train/G1032/hidden/t31_hidden.clas.abap @@ -0,0 +1,112 @@ +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 row_count FOR TESTING. + METHODS count_of_busy_tutor FOR TESTING. + METHODS minutes_of_busy_tutor FOR TESTING. + METHODS count_of_top_tutor FOR TESTING. + METHODS minutes_of_top_tutor FOR TESTING. + METHODS zero_minutes_ignored FOR TESTING. + METHODS zero_minutes_reduce_count FOR TESTING. + METHODS tutor_with_two_lessons FOR TESTING. + METHODS tutor_without_lessons FOR TESTING. + METHODS name_is_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TUTOR_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_tutor TYPE STANDARD TABLE OF {{p}}tutor WITH EMPTY KEY. + DATA lt_lesson TYPE STANDARD TABLE OF {{p}}lesson WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_tutor = VALUE #( ( tutor_id = 'T1' tutor_name = 'ALICE' subject = 'PIANO' ) + ( tutor_id = 'T2' tutor_name = 'BOB' subject = 'GUITAR' ) + ( tutor_id = 'T3' tutor_name = 'CARLA' subject = 'VIOLIN' ) + ( tutor_id = 'T4' tutor_name = 'DAVE' subject = 'DRUMS' ) + ( tutor_id = 'T5' tutor_name = 'EMMA' subject = 'FLUTE' ) + ( tutor_id = 'T6' tutor_name = 'FELIX' subject = 'CELLO' ) ). + lt_lesson = VALUE #( ( lesson_id = 'L01' tutor_id = 'T1' dur_min = 30 ) + ( lesson_id = 'L02' tutor_id = 'T1' dur_min = 45 ) + ( lesson_id = 'L03' tutor_id = 'T1' dur_min = 45 ) + ( lesson_id = 'L04' tutor_id = 'T2' dur_min = 60 ) + ( lesson_id = 'L05' tutor_id = 'T2' dur_min = 60 ) + ( lesson_id = 'L06' tutor_id = 'T2' dur_min = 30 ) + ( lesson_id = 'L07' tutor_id = 'T2' dur_min = 15 ) + ( lesson_id = 'L08' tutor_id = 'T4' dur_min = 30 ) + ( lesson_id = 'L09' tutor_id = 'T4' dur_min = 30 ) + ( lesson_id = 'L10' tutor_id = 'T5' dur_min = 30 ) + ( lesson_id = 'L11' tutor_id = 'T5' dur_min = 30 ) + ( lesson_id = 'L12' tutor_id = 'T5' dur_min = 0 ) + ( lesson_id = 'L13' tutor_id = 'T6' dur_min = 30 ) + ( lesson_id = 'L14' tutor_id = 'T6' dur_min = 30 ) + ( lesson_id = 'L15' tutor_id = 'T6' dur_min = 30 ) + ( lesson_id = 'L16' tutor_id = 'T6' dur_min = 0 ) ). + go_env->insert_test_data( lt_tutor ). + go_env->insert_test_data( lt_lesson ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_tutor_load INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = CONV i( lv_rows ) ). + ENDMETHOD. + + METHOD count_of_busy_tutor. + SELECT SINGLE lesson_count FROM {{p}}i_tutor_load WHERE tutor_id = 'T1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = CONV i( lv_count ) ). + ENDMETHOD. + + METHOD minutes_of_busy_tutor. + SELECT SINGLE total_minutes FROM {{p}}i_tutor_load WHERE tutor_id = 'T1' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD count_of_top_tutor. + SELECT SINGLE lesson_count FROM {{p}}i_tutor_load WHERE tutor_id = 'T2' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 4 act = CONV i( lv_count ) ). + ENDMETHOD. + + METHOD minutes_of_top_tutor. + SELECT SINGLE total_minutes FROM {{p}}i_tutor_load WHERE tutor_id = 'T2' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 165 ) act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD zero_minutes_ignored. + SELECT SINGLE lesson_count, total_minutes FROM {{p}}i_tutor_load + WHERE tutor_id = 'T6' INTO @DATA(ls_load). + cl_abap_unit_assert=>assert_equals( exp = 3 act = CONV i( ls_load-lesson_count ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 90 ) + act = CONV decfloat34( ls_load-total_minutes ) ). + ENDMETHOD. + + METHOD zero_minutes_reduce_count. + SELECT COUNT(*) FROM {{p}}i_tutor_load WHERE tutor_id = 'T5' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = CONV i( lv_rows ) ). + ENDMETHOD. + + METHOD tutor_with_two_lessons. + SELECT COUNT(*) FROM {{p}}i_tutor_load WHERE tutor_id = 'T4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = CONV i( lv_rows ) ). + ENDMETHOD. + + METHOD tutor_without_lessons. + SELECT COUNT(*) FROM {{p}}i_tutor_load WHERE tutor_id = 'T3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = CONV i( lv_rows ) ). + ENDMETHOD. + + METHOD name_is_shown. + SELECT SINGLE tutor_name FROM {{p}}i_tutor_load WHERE tutor_id = 'T2' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'BOB' act = lv_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1032/mutation.json b/tasks_gen/train/G1032/mutation.json new file mode 100644 index 0000000..7c830eb --- /dev/null +++ b/tasks_gen/train/G1032/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G1032", + "mutants": [ + { + "object": "{{P}}I_TUTOR_LOAD", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + }, + { + "object": "{{P}}I_TUTOR_LOAD", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "COUNT_OF_BUSY_TUTOR", + "COUNT_OF_TOP_TUTOR", + "MINUTES_OF_BUSY_TUTOR", + "MINUTES_OF_TOP_TUTOR", + "ROW_COUNT", + "TUTOR_WITHOUT_LESSONS", + "TUTOR_WITH_TWO_LESSONS", + "ZERO_MINUTES_IGNORED", + "ZERO_MINUTES_REDUCE_COUNT" + ] + }, + { + "object": "{{P}}I_TUTOR_LOAD", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "MINUTES_OF_BUSY_TUTOR", + "MINUTES_OF_TOP_TUTOR", + "ZERO_MINUTES_IGNORED" + ] + }, + { + "object": "{{P}}I_TUTOR_LOAD", + "mutant": "line 12: > -> <= (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "COUNT_OF_BUSY_TUTOR", + "COUNT_OF_TOP_TUTOR", + "MINUTES_OF_BUSY_TUTOR", + "MINUTES_OF_TOP_TUTOR", + "NAME_IS_SHOWN", + "ROW_COUNT", + "ZERO_MINUTES_IGNORED" + ] + }, + { + "object": "{{P}}I_TUTOR_LOAD", + "mutant": "line 16: 3 -> 4 (const)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "COUNT_OF_BUSY_TUTOR", + "MINUTES_OF_BUSY_TUTOR", + "ROW_COUNT", + "ZERO_MINUTES_IGNORED" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1032/reference/i_tutor_load.ddls.asddls b/tasks_gen/train/G1032/reference/i_tutor_load.ddls.asddls new file mode 100644 index 0000000..703c25a --- /dev/null +++ b/tasks_gen/train/G1032/reference/i_tutor_load.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Tutor load' +define view entity {{P}}I_TUTOR_LOAD + as select from {{p}}tutor as tutor + inner join {{p}}lesson as lesson on lesson.tutor_id = tutor.tutor_id +{ + key tutor.tutor_id as tutor_id, + tutor.tutor_name as tutor_name, + count( * ) as lesson_count, + sum( lesson.dur_min ) as total_minutes +} +where lesson.dur_min > 0 +group by + tutor.tutor_id, + tutor.tutor_name +having count( * ) >= 3 diff --git a/tasks_gen/train/G1032/reference/t31_test.clas.abap b/tasks_gen/train/G1032/reference/t31_test.clas.abap new file mode 100644 index 0000000..9ce8754 --- /dev/null +++ b/tasks_gen/train/G1032/reference/t31_test.clas.abap @@ -0,0 +1,40 @@ +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 load_of_one_tutor FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_TUTOR_LOAD' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD load_of_one_tutor. + DATA lt_tutor TYPE STANDARD TABLE OF {{p}}tutor WITH EMPTY KEY. + DATA lt_lesson TYPE STANDARD TABLE OF {{p}}lesson WITH EMPTY KEY. + lt_tutor = VALUE #( ( tutor_id = 'A' tutor_name = 'ANNA' subject = 'PIANO' ) ). + lt_lesson = VALUE #( ( lesson_id = 'X1' tutor_id = 'A' dur_min = 20 ) + ( lesson_id = 'X2' tutor_id = 'A' dur_min = 40 ) + ( lesson_id = 'X3' tutor_id = 'A' dur_min = 60 ) ). + go_env->insert_test_data( lt_tutor ). + go_env->insert_test_data( lt_lesson ). + SELECT SINGLE lesson_count, total_minutes FROM {{p}}i_tutor_load + WHERE tutor_id = 'A' INTO @DATA(ls_load). + cl_abap_unit_assert=>assert_equals( exp = 3 act = CONV i( ls_load-lesson_count ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) + act = CONV decfloat34( ls_load-total_minutes ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1032/seed/lesson.tabl.asabap b/tasks_gen/train/G1032/seed/lesson.tabl.asabap new file mode 100644 index 0000000..87e16cd --- /dev/null +++ b/tasks_gen/train/G1032/seed/lesson.tabl.asabap @@ -0,0 +1,12 @@ +@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; + tutor_id : abap.char(10); + lesson_date : abap.dats; + dur_min : abap.int4; +} diff --git a/tasks_gen/train/G1032/seed/tutor.tabl.asabap b/tasks_gen/train/G1032/seed/tutor.tabl.asabap new file mode 100644 index 0000000..37f4377 --- /dev/null +++ b/tasks_gen/train/G1032/seed/tutor.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Tutor' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}tutor { + key client : abap.clnt not null; + key tutor_id : abap.char(10) not null; + tutor_name : abap.char(30); + subject : abap.char(20); +} diff --git a/tasks_gen/train/G1032/spec.md b/tasks_gen/train/G1032/spec.md new file mode 100644 index 0000000..b934c94 --- /dev/null +++ b/tasks_gen/train/G1032/spec.md @@ -0,0 +1,39 @@ +# 1. Goal +Show the workload of each tutor of a music school. A report and an OData +service will read the workload, so the calculation must be in the database, +in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}TUTOR (tutor master data), package $TMP. Key: TUTOR_ID (CHAR 10). + Fields: TUTOR_NAME (CHAR 30), SUBJECT (CHAR 20). +- Table {{P}}LESSON (the lessons that the tutors give), package $TMP. + Key: LESSON_ID (CHAR 10). Fields: TUTOR_ID (CHAR 10), LESSON_DATE (DATS), + DUR_MIN (INT4, the length of the lesson in minutes). + +# 4. Contract +- Create the CDS view entity {{P}}I_TUTOR_LOAD in package $TMP. +- Elements, with these names: TUTOR_ID (key), TUTOR_NAME, LESSON_COUNT, + TOTAL_MINUTES. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Count only lessons with DUR_MIN greater than 0. A lesson with DUR_MIN = 0 + is not a lesson of this view. It does not count and it does not add minutes. +2. Show one row for each tutor who has at least 3 lessons. Do not show a tutor + with fewer than 3 lessons. Do not show a tutor without lessons. +3. LESSON_COUNT is the number of lessons of the tutor. +4. TOTAL_MINUTES is the sum of the DUR_MIN values of all lessons of the tutor. +5. TUTOR_NAME is the name of the tutor. + +# 6. Constraints +- Release target: 8.16 (SAP_BASIS 816). +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}TUTOR 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/G1032/task.json b/tasks_gen/train/G1032/task.json new file mode 100644 index 0000000..1c680e6 --- /dev/null +++ b/tasks_gen/train/G1032/task.json @@ -0,0 +1,65 @@ +{ + "id": "G1032", + "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}}TUTOR", + "file": "seed/tutor.tabl.asabap", + "description": "Tutor master data" + }, + { + "type": "TABL", + "name": "{{P}}LESSON", + "file": "seed/lesson.tabl.asabap", + "description": "Lessons that the tutors give" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_TUTOR_LOAD", + "fields": [ + "TUTOR_ID", + "TUTOR_NAME", + "LESSON_COUNT", + "TOTAL_MINUTES" + ] + } + ], + "out_of_scope": [ + "{{P}}TUTOR", + "{{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_TUTOR_LOAD", + "file": "reference/i_tutor_load.ddls.asddls", + "description": "Workload of each tutor" + }, + { + "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/G1033/faulty/m0_oil_settlement.clas.abap b/tasks_gen/train/G1033/faulty/m0_oil_settlement.clas.abap new file mode 100644 index 0000000..c02b94e --- /dev/null +++ b/tasks_gen/train/G1033/faulty/m0_oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL AND ls_collection-litres <= 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres + ls_collection-litres. + -round_count = -round_count + 1. + IF ls_collection-collected_on > -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/faulty/m1_oil_settlement.clas.abap b/tasks_gen/train/G1033/faulty/m1_oil_settlement.clas.abap new file mode 100644 index 0000000..85235de --- /dev/null +++ b/tasks_gen/train/G1033/faulty/m1_oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL OR ls_collection-litres > 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres + ls_collection-litres. + -round_count = -round_count + 1. + IF ls_collection-collected_on > -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/faulty/m2_oil_settlement.clas.abap b/tasks_gen/train/G1033/faulty/m2_oil_settlement.clas.abap new file mode 100644 index 0000000..9486bfd --- /dev/null +++ b/tasks_gen/train/G1033/faulty/m2_oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres - ls_collection-litres. + -round_count = -round_count + 1. + IF ls_collection-collected_on > -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/faulty/m3_oil_settlement.clas.abap b/tasks_gen/train/G1033/faulty/m3_oil_settlement.clas.abap new file mode 100644 index 0000000..f2f9e81 --- /dev/null +++ b/tasks_gen/train/G1033/faulty/m3_oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres + ls_collection-litres. + -round_count = -round_count - 1. + IF ls_collection-collected_on > -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/faulty/m4_oil_settlement.clas.abap b/tasks_gen/train/G1033/faulty/m4_oil_settlement.clas.abap new file mode 100644 index 0000000..133c243 --- /dev/null +++ b/tasks_gen/train/G1033/faulty/m4_oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres + ls_collection-litres. + -round_count = -round_count + 1. + IF ls_collection-collected_on <= -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/generation.json b/tasks_gen/train/G1033/generation.json new file mode 100644 index 0000000..48cf6b7 --- /dev/null +++ b/tasks_gen/train/G1033/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1033", + "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/G1033/hidden/t_oil_hidden.clas.abap b/tasks_gen/train/G1033/hidden/t_oil_hidden.clas.abap new file mode 100644 index 0000000..235244d --- /dev/null +++ b/tasks_gen/train/G1033/hidden/t_oil_hidden.clas.abap @@ -0,0 +1,134 @@ +CLASS {{p}}t_oil_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}oil_settlement. + + METHODS setup. + METHODS sums_same_restaurant FOR TESTING. + METHODS separates_restaurants FOR TESTING. + METHODS ignores_zero_litres FOR TESTING. + METHODS ignores_negative_litres FOR TESTING. + METHODS ignores_empty_name FOR TESTING. + METHODS normalises_name FOR TESTING. + METHODS keeps_latest_date FOR TESTING. + METHODS sorts_by_restaurant FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS all_invalid FOR TESTING. + METHODS find_row + IMPORTING it_result TYPE {{p}}oil_settlement=>tt_settlements + iv_restaurant TYPE {{p}}oil_settlement=>ty_restaurant + RETURNING VALUE(rs_row) TYPE {{p}}oil_settlement=>ty_settlement. +ENDCLASS. + + +CLASS {{p}}t_oil_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}oil_settlement( ). + ENDMETHOD. + + METHOD sums_same_restaurant. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 100 ) + ( restaurant = 'R001' collected_on = '20240112' litres = 50 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-round_count ). + ENDMETHOD. + + METHOD separates_restaurants. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 100 ) + ( restaurant = 'R002' collected_on = '20240110' litres = 200 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + DATA(ls_r001) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = ls_r001-total_litres ). + DATA(ls_r002) = find_row( it_result = lt_result iv_restaurant = 'R002' ). + cl_abap_unit_assert=>assert_equals( exp = 200 act = ls_r002-total_litres ). + ENDMETHOD. + + METHOD ignores_zero_litres. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 0 ) + ( restaurant = 'R002' collected_on = '20240110' litres = 30 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R002' ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ). + ENDMETHOD. + + METHOD ignores_negative_litres. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = -5 ) + ( restaurant = 'R001' collected_on = '20240111' litres = 40 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 40 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ). + ENDMETHOD. + + METHOD ignores_empty_name. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = space collected_on = '20240110' litres = 100 ) + ( restaurant = ' ' collected_on = '20240110' litres = 100 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD normalises_name. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'r001' collected_on = '20240110' litres = 100 ) + ( restaurant = ' R001 ' collected_on = '20240111' litres = 50 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 'R001' act = ls_row-restaurant ). + ENDMETHOD. + + METHOD keeps_latest_date. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R001' collected_on = '20240305' litres = 10 ) + ( restaurant = 'R001' collected_on = '20240201' litres = 10 ) ) ). + DATA lv_expected TYPE d. + lv_expected = '20240305'. + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_row-last_collection ). + ENDMETHOD. + + METHOD sorts_by_restaurant. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R003' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R001' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R002' collected_on = '20240110' litres = 10 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + DATA lv_order TYPE string. + LOOP AT lt_result INTO DATA(ls_row). + lv_order = lv_order && ls_row-restaurant+0(4). + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = 'R001R002R003' act = lv_order ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_empty TYPE {{p}}oil_settlement=>tt_collections. + DATA(lt_result) = mo_cut->settle( lt_empty ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD all_invalid. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 0 ) + ( restaurant = space collected_on = '20240110' litres = 50 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD find_row. + DATA ls_row TYPE {{p}}oil_settlement=>ty_settlement. + DATA lv_found TYPE abap_bool. + LOOP AT it_result INTO ls_row WHERE restaurant = iv_restaurant. + rs_row = ls_row. + lv_found = abap_true. + EXIT. + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_found ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/mutation.json b/tasks_gen/train/G1033/mutation.json new file mode 100644 index 0000000..a52cd57 --- /dev/null +++ b/tasks_gen/train/G1033/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1033", + "mutants": [ + { + "object": "{{P}}OIL_SETTLEMENT", + "mutant": "line 51: OR -> AND (logic)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "ALL_INVALID", + "IGNORES_EMPTY_NAME", + "IGNORES_NEGATIVE_LITRES", + "IGNORES_ZERO_LITRES" + ] + }, + { + "object": "{{P}}OIL_SETTLEMENT", + "mutant": "line 51: <= -> > (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "ALL_INVALID", + "IGNORES_NEGATIVE_LITRES", + "IGNORES_ZERO_LITRES", + "KEEPS_LATEST_DATE", + "NORMALISES_NAME", + "SEPARATES_RESTAURANTS", + "SORTS_BY_RESTAURANT", + "SUMS_SAME_RESTAURANT" + ] + }, + { + "object": "{{P}}OIL_SETTLEMENT", + "mutant": "line 60: + -> - (arith)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "IGNORES_NEGATIVE_LITRES", + "IGNORES_ZERO_LITRES", + "NORMALISES_NAME", + "SEPARATES_RESTAURANTS", + "SUMS_SAME_RESTAURANT" + ] + }, + { + "object": "{{P}}OIL_SETTLEMENT", + "mutant": "line 61: + -> - (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "IGNORES_NEGATIVE_LITRES", + "IGNORES_ZERO_LITRES", + "SUMS_SAME_RESTAURANT" + ] + }, + { + "object": "{{P}}OIL_SETTLEMENT", + "mutant": "line 62: > -> <= (rel)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "KEEPS_LATEST_DATE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1033/reference/oil_settlement.clas.abap b/tasks_gen/train/G1033/reference/oil_settlement.clas.abap new file mode 100644 index 0000000..b19bdfd --- /dev/null +++ b/tasks_gen/train/G1033/reference/oil_settlement.clas.abap @@ -0,0 +1,73 @@ +CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_restaurant TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_collection, + restaurant TYPE ty_restaurant, + collected_on TYPE d, + litres TYPE i, + END OF ty_collection. + + TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_settlement, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_settlement. + + TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant. + + METHODS settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_buffer, + restaurant TYPE ty_restaurant, + total_litres TYPE i, + round_count TYPE i, + last_collection TYPE d, + END OF ty_buffer. + + TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant. + + METHODS normalize + IMPORTING iv_restaurant TYPE ty_restaurant + RETURNING VALUE(rv_restaurant) TYPE ty_restaurant. +ENDCLASS. + + +CLASS {{p}}oil_settlement IMPLEMENTATION. + METHOD settle. + DATA lt_buffer TYPE tt_buffer. + + LOOP AT it_collections INTO DATA(ls_collection). + DATA(lv_restaurant) = normalize( ls_collection-restaurant ). + IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0. + CONTINUE. + ENDIF. + + ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING . + ENDIF. + + -total_litres = -total_litres + ls_collection-litres. + -round_count = -round_count + 1. + IF ls_collection-collected_on > -last_collection. + -last_collection = ls_collection-collected_on. + ENDIF. + ENDLOOP. + + rt_settlements = CORRESPONDING #( lt_buffer ). + ENDMETHOD. + + METHOD normalize. + rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/reference/oil_settlement.testclasses.abap b/tasks_gen/train/G1033/reference/oil_settlement.testclasses.abap new file mode 100644 index 0000000..28ba271 --- /dev/null +++ b/tasks_gen/train/G1033/reference/oil_settlement.testclasses.abap @@ -0,0 +1,86 @@ +CLASS ltc_oil_settlement DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}oil_settlement. + + METHODS setup. + METHODS sums_same_restaurant FOR TESTING. + METHODS ignores_invalid_records FOR TESTING. + METHODS normalises_restaurant FOR TESTING. + METHODS keeps_latest_date FOR TESTING. + METHODS sorts_result FOR TESTING. + METHODS find_row + IMPORTING it_result TYPE {{p}}oil_settlement=>tt_settlements + iv_restaurant TYPE {{p}}oil_settlement=>ty_restaurant + RETURNING VALUE(rs_row) TYPE {{p}}oil_settlement=>ty_settlement. +ENDCLASS. + + +CLASS ltc_oil_settlement IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}oil_settlement( ). + ENDMETHOD. + + METHOD sums_same_restaurant. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 100 ) + ( restaurant = 'R001' collected_on = '20240115' litres = 50 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-round_count ). + ENDMETHOD. + + METHOD ignores_invalid_records. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 0 ) + ( restaurant = space collected_on = '20240110' litres = 20 ) + ( restaurant = 'R002' collected_on = '20240110' litres = 30 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R002' ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ). + ENDMETHOD. + + METHOD normalises_restaurant. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'r001' collected_on = '20240110' litres = 10 ) + ( restaurant = ' R001 ' collected_on = '20240111' litres = 20 ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ). + cl_abap_unit_assert=>assert_equals( exp = 'R001' act = ls_row-restaurant ). + ENDMETHOD. + + METHOD keeps_latest_date. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R001' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R001' collected_on = '20240305' litres = 10 ) ) ). + DATA lv_expected TYPE d. + lv_expected = '20240305'. + DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_row-last_collection ). + ENDMETHOD. + + METHOD sorts_result. + DATA(lt_result) = mo_cut->settle( VALUE #( + ( restaurant = 'R003' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R001' collected_on = '20240110' litres = 10 ) + ( restaurant = 'R002' collected_on = '20240110' litres = 10 ) ) ). + DATA lv_order TYPE string. + LOOP AT lt_result INTO DATA(ls_row). + lv_order = lv_order && ls_row-restaurant+0(4). + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = 'R001R002R003' act = lv_order ). + ENDMETHOD. + + METHOD find_row. + DATA ls_row TYPE {{p}}oil_settlement=>ty_settlement. + DATA lv_found TYPE abap_bool. + LOOP AT it_result INTO ls_row WHERE restaurant = iv_restaurant. + rs_row = ls_row. + lv_found = abap_true. + EXIT. + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_found ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1033/spec.md b/tasks_gen/train/G1033/spec.md new file mode 100644 index 0000000..693eae7 --- /dev/null +++ b/tasks_gen/train/G1033/spec.md @@ -0,0 +1,68 @@ +# 1. Goal + +A company collects used cooking oil from restaurants. The company pays the +restaurants for the oil. Each collection is recorded with the restaurant, the +collection date and the volume in litres. A settlement program calls the class +once for each settlement run. The class returns one row for each restaurant. + +# 2. Open questions + +None. + +# 3. Context + +- The class {{P}}OIL_SETTLEMENT does not exist. Create it in package $TMP. +- The caller sends a list of collection records. One restaurant can appear in + more than one record. +- The class does not read the database. All data comes from the caller. + +# 4. Contract + +- Create the class {{P}}OIL_SETTLEMENT in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - ty_restaurant: character, length 10. + - ty_collection: structure with the components restaurant (ty_restaurant), + collected_on (date) and litres (integer). + - tt_collections: table of ty_collection. The table can hold more than one row + for the same restaurant. + - ty_settlement: structure with the components restaurant (ty_restaurant), + total_litres (integer), round_count (integer) and last_collection (date). + - tt_settlements: table of ty_settlement. +- The class has this public method: + settle + IMPORTING it_collections TYPE tt_collections + RETURNING VALUE(rt_settlements) TYPE tt_settlements. +- Do not add other public methods. + +# 5. Business rules + +1. Two restaurant names are the same when they are equal after you remove all + spaces and convert the letters to upper case. A restaurant is empty when it + contains only spaces. +2. A collection record is valid when its restaurant is not empty and its volume + is greater than zero. +3. Ignore every record that is not valid. An ignored record does not change a + total and it does not increase a counter. +4. The result has exactly one row for each restaurant that has at least one + valid record. +5. A result row has these values: + - restaurant: the name of the valid records, without spaces and in upper case. + - total_litres: the sum of the volumes of the valid records. + - round_count: the number of the valid records. + - last_collection: the latest collection date of the valid records. +6. The result is sorted in ascending order of restaurant. +7. If there is no valid record, the result is empty. + +# 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 access, no ALV, no dynpro, no RAP. + +# 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/G1033/task.json b/tasks_gen/train/G1033/task.json new file mode 100644 index 0000000..5eafd90 --- /dev/null +++ b/tasks_gen/train/G1033/task.json @@ -0,0 +1,42 @@ +{ + "id": "G1033", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 20 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}OIL_SETTLEMENT" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_OIL_HIDDEN", + "file": "hidden/t_oil_hidden.clas.abap", + "description": "Hidden ABAP Unit tests for the used cooking oil settlement class" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}OIL_SETTLEMENT", + "file": "reference/oil_settlement.clas.abap", + "description": "Settlement class that aggregates oil collections per restaurant", + "testclasses_file": "reference/oil_settlement.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1034/faulty/m0_storage_pricer.clas.abap b/tasks_gen/train/G1034/faulty/m0_storage_pricer.clas.abap new file mode 100644 index 0000000..78ed0e1 --- /dev/null +++ b/tasks_gen/train/G1034/faulty/m0_storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) <> abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months > 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate + c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months >= c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_true. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/faulty/m1_storage_pricer.clas.abap b/tasks_gen/train/G1034/faulty/m1_storage_pricer.clas.abap new file mode 100644 index 0000000..c89bcab --- /dev/null +++ b/tasks_gen/train/G1034/faulty/m1_storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) = abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months <= 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate + c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months >= c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_true. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/faulty/m2_storage_pricer.clas.abap b/tasks_gen/train/G1034/faulty/m2_storage_pricer.clas.abap new file mode 100644 index 0000000..2bf1f13 --- /dev/null +++ b/tasks_gen/train/G1034/faulty/m2_storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) = abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months > 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate - c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months >= c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_true. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/faulty/m3_storage_pricer.clas.abap b/tasks_gen/train/G1034/faulty/m3_storage_pricer.clas.abap new file mode 100644 index 0000000..c9e4680 --- /dev/null +++ b/tasks_gen/train/G1034/faulty/m3_storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) = abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months > 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate + c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months < c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_true. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/faulty/m4_storage_pricer.clas.abap b/tasks_gen/train/G1034/faulty/m4_storage_pricer.clas.abap new file mode 100644 index 0000000..fc5431a --- /dev/null +++ b/tasks_gen/train/G1034/faulty/m4_storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) = abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months > 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate + c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months >= c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_false. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/generation.json b/tasks_gen/train/G1034/generation.json new file mode 100644 index 0000000..d4804cd --- /dev/null +++ b/tasks_gen/train/G1034/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1034", + "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/G1034/hidden/t_stor_hidden.clas.abap b/tasks_gen/train/G1034/hidden/t_stor_hidden.clas.abap new file mode 100644 index 0000000..6fcae9e --- /dev/null +++ b/tasks_gen/train/G1034/hidden/t_stor_hidden.clas.abap @@ -0,0 +1,205 @@ +CLASS {{p}}t_stor_hidden DEFINITION + PUBLIC + FINAL + CREATE PUBLIC + FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}storage_pricer. + + METHODS setup. + + METHODS booking + IMPORTING iv_size TYPE i + iv_months TYPE i + iv_climate TYPE abap_bool + iv_insurance TYPE abap_bool + RETURNING VALUE(rs_booking) TYPE {{p}}storage_pricer=>ty_booking. + + METHODS assert_price + IMPORTING iv_size TYPE i + iv_months TYPE i + iv_climate TYPE abap_bool + iv_insurance TYPE abap_bool + iv_expected TYPE {{p}}storage_pricer=>ty_amount + iv_msg TYPE string OPTIONAL. + + METHODS rejects_zero_size FOR TESTING. + METHODS rejects_zero_months FOR TESTING. + METHODS rejects_negative FOR TESTING. + METHODS small_unit_short FOR TESTING. + METHODS size_band_limits FOR TESTING. + METHODS climate_surcharge FOR TESTING. + METHODS insurance_surcharge FOR TESTING. + METHODS discount_tiers FOR TESTING. + METHODS discount_thresholds FOR TESTING. + METHODS all_extras FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_stor_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}storage_pricer( ). + ENDMETHOD. + + METHOD booking. + rs_booking-size_sqm = iv_size. + rs_booking-months = iv_months. + rs_booking-climate = iv_climate. + rs_booking-insurance = iv_insurance. + ENDMETHOD. + + METHOD assert_price. + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_price( + booking( iv_size = iv_size + iv_months = iv_months + iv_climate = iv_climate + iv_insurance = iv_insurance ) ) + msg = iv_msg ). + ENDMETHOD. + + METHOD rejects_zero_size. + assert_price( iv_size = 0 + iv_months = 6 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '0.00' + iv_msg = `zero size` ). + ENDMETHOD. + + METHOD rejects_zero_months. + assert_price( iv_size = 5 + iv_months = 0 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '0.00' + iv_msg = `zero months` ). + ENDMETHOD. + + METHOD rejects_negative. + assert_price( iv_size = -3 + iv_months = 4 + iv_climate = abap_true + iv_insurance = abap_true + iv_expected = '0.00' + iv_msg = `negative size` ). + assert_price( iv_size = 4 + iv_months = -1 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '0.00' + iv_msg = `negative months` ). + ENDMETHOD. + + METHOD small_unit_short. + assert_price( iv_size = 3 + iv_months = 1 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '61.00' ). + ENDMETHOD. + + METHOD size_band_limits. + assert_price( iv_size = 5 + iv_months = 2 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '159.00' + iv_msg = `size 5` ). + assert_price( iv_size = 6 + iv_months = 2 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '157.00' + iv_msg = `size 6` ). + assert_price( iv_size = 15 + iv_months = 2 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '364.00' + iv_msg = `size 15` ). + assert_price( iv_size = 16 + iv_months = 2 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '307.00' + iv_msg = `size 16` ). + ENDMETHOD. + + METHOD climate_surcharge. + assert_price( iv_size = 4 + iv_months = 3 + iv_climate = abap_true + iv_insurance = abap_false + iv_expected = '241.00' ). + ENDMETHOD. + + METHOD insurance_surcharge. + assert_price( iv_size = 6 + iv_months = 5 + iv_climate = abap_false + iv_insurance = abap_true + iv_expected = '391.00' ). + ENDMETHOD. + + METHOD discount_tiers. + assert_price( iv_size = 10 + iv_months = 6 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '674.50' + iv_msg = `6 months` ). + assert_price( iv_size = 20 + iv_months = 12 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '1963.00' + iv_msg = `12 months` ). + assert_price( iv_size = 20 + iv_months = 24 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '3691.00' + iv_msg = `24 months` ). + ENDMETHOD. + + METHOD discount_thresholds. + assert_price( iv_size = 10 + iv_months = 5 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '594.00' + iv_msg = `5 months` ). + assert_price( iv_size = 10 + iv_months = 11 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '1220.75' + iv_msg = `11 months` ). + assert_price( iv_size = 10 + iv_months = 23 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '2399.50' + iv_msg = `23 months` ). + ENDMETHOD. + + METHOD all_extras. + assert_price( iv_size = 5 + iv_months = 6 + iv_climate = abap_true + iv_insurance = abap_true + iv_expected = '573.25' + iv_msg = `medium unit` ). + assert_price( iv_size = 16 + iv_months = 12 + iv_climate = abap_true + iv_insurance = abap_true + iv_expected = '2524.60' + iv_msg = `large unit` ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/mutation.json b/tasks_gen/train/G1034/mutation.json new file mode 100644 index 0000000..e8e502c --- /dev/null +++ b/tasks_gen/train/G1034/mutation.json @@ -0,0 +1,84 @@ +{ + "task": "G1034", + "mutants": [ + { + "object": "{{P}}STORAGE_PRICER", + "mutant": "line 61: = -> <> (eq)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "ALL_EXTRAS", + "CLIMATE_SURCHARGE", + "DISCOUNT_THRESHOLDS", + "DISCOUNT_TIERS", + "INSURANCE_SURCHARGE", + "REJECTS_NEGATIVE", + "REJECTS_ZERO_MONTHS", + "REJECTS_ZERO_SIZE", + "SIZE_BAND_LIMITS", + "SMALL_UNIT_SHORT" + ] + }, + { + "object": "{{P}}STORAGE_PRICER", + "mutant": "line 76: > -> <= (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "ALL_EXTRAS", + "CLIMATE_SURCHARGE", + "DISCOUNT_THRESHOLDS", + "DISCOUNT_TIERS", + "INSURANCE_SURCHARGE", + "REJECTS_NEGATIVE", + "REJECTS_ZERO_MONTHS", + "SIZE_BAND_LIMITS", + "SMALL_UNIT_SHORT" + ] + }, + { + "object": "{{P}}STORAGE_PRICER", + "mutant": "line 89: + -> - (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "ALL_EXTRAS", + "CLIMATE_SURCHARGE" + ] + }, + { + "object": "{{P}}STORAGE_PRICER", + "mutant": "line 94: >= -> < (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "ALL_EXTRAS", + "CLIMATE_SURCHARGE", + "DISCOUNT_THRESHOLDS", + "DISCOUNT_TIERS", + "INSURANCE_SURCHARGE", + "SIZE_BAND_LIMITS", + "SMALL_UNIT_SHORT" + ] + }, + { + "object": "{{P}}STORAGE_PRICER", + "mutant": "line 104: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "ALL_EXTRAS", + "CLIMATE_SURCHARGE", + "DISCOUNT_THRESHOLDS", + "DISCOUNT_TIERS", + "INSURANCE_SURCHARGE", + "SIZE_BAND_LIMITS", + "SMALL_UNIT_SHORT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1034/reference/storage_pricer.clas.abap b/tasks_gen/train/G1034/reference/storage_pricer.clas.abap new file mode 100644 index 0000000..39f7237 --- /dev/null +++ b/tasks_gen/train/G1034/reference/storage_pricer.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_small TYPE ty_amount VALUE '14.00', + c_rate_medium TYPE ty_amount VALUE '11.50', + c_rate_large TYPE ty_amount VALUE '9.00', + c_climate_addon TYPE ty_amount VALUE '4.50', + c_insurance_rate TYPE ty_amount VALUE '0.90', + c_admin_fee TYPE ty_amount VALUE '19.00', + c_percent TYPE i VALUE 100, + c_size_small_max TYPE ty_sqm VALUE 5, + c_size_medium_max TYPE ty_sqm VALUE 15, + c_months_short TYPE ty_months VALUE 6, + c_months_medium TYPE ty_months VALUE 12, + c_months_long TYPE ty_months VALUE 24, + c_disc_short TYPE i VALUE 5, + c_disc_medium TYPE i VALUE 10, + c_disc_long TYPE i VALUE 15. + + METHODS is_valid + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS monthly_rate + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_rate) TYPE ty_amount. + + METHODS discount_percent + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_percent) TYPE i. + + METHODS insurance_fee + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + IF is_valid( is_booking ) = abap_false. + RETURN. + ENDIF. + + DATA lv_rent TYPE ty_amount. + DATA lv_discount TYPE ty_amount. + DATA lv_total TYPE ty_amount. + + lv_rent = is_booking-size_sqm * is_booking-months * monthly_rate( is_booking ). + lv_discount = lv_rent * discount_percent( is_booking ) / c_percent. + lv_total = lv_rent - lv_discount + insurance_fee( is_booking ) + c_admin_fee. + rv_price = lv_total. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_booking-size_sqm > 0 AND is_booking-months > 0 ). + ENDMETHOD. + + METHOD monthly_rate. + IF is_booking-size_sqm <= c_size_small_max. + rv_rate = c_rate_small. + ELSEIF is_booking-size_sqm <= c_size_medium_max. + rv_rate = c_rate_medium. + ELSE. + rv_rate = c_rate_large. + ENDIF. + + IF is_booking-climate = abap_true. + rv_rate = rv_rate + c_climate_addon. + ENDIF. + ENDMETHOD. + + METHOD discount_percent. + IF is_booking-months >= c_months_long. + rv_percent = c_disc_long. + ELSEIF is_booking-months >= c_months_medium. + rv_percent = c_disc_medium. + ELSEIF is_booking-months >= c_months_short. + rv_percent = c_disc_short. + ENDIF. + ENDMETHOD. + + METHOD insurance_fee. + IF is_booking-insurance = abap_true. + rv_fee = is_booking-size_sqm * is_booking-months * c_insurance_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/reference/storage_pricer.testclasses.abap b/tasks_gen/train/G1034/reference/storage_pricer.testclasses.abap new file mode 100644 index 0000000..d82e267 --- /dev/null +++ b/tasks_gen/train/G1034/reference/storage_pricer.testclasses.abap @@ -0,0 +1,92 @@ +CLASS ltc_storage_pricer DEFINITION + FINAL + FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}storage_pricer. + + METHODS setup. + + METHODS assert_price + IMPORTING iv_size TYPE i + iv_months TYPE i + iv_climate TYPE abap_bool + iv_insurance TYPE abap_bool + iv_expected TYPE {{p}}storage_pricer=>ty_amount. + + METHODS small_unit_no_extras FOR TESTING. + METHODS medium_unit_climate FOR TESTING. + METHODS large_unit_all_extras FOR TESTING. + METHODS long_term_discount FOR TESTING. + METHODS size_band_change FOR TESTING. + METHODS invalid_booking FOR TESTING. +ENDCLASS. + + +CLASS ltc_storage_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}storage_pricer( ). + ENDMETHOD. + + METHOD assert_price. + DATA(ls_booking) = VALUE {{p}}storage_pricer=>ty_booking( + size_sqm = iv_size + months = iv_months + climate = iv_climate + insurance = iv_insurance ). + + cl_abap_unit_assert=>assert_equals( + exp = iv_expected + act = mo_cut->calculate_price( ls_booking ) ). + ENDMETHOD. + + METHOD small_unit_no_extras. + assert_price( iv_size = 3 + iv_months = 1 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '61.00' ). + ENDMETHOD. + + METHOD medium_unit_climate. + assert_price( iv_size = 4 + iv_months = 3 + iv_climate = abap_true + iv_insurance = abap_false + iv_expected = '241.00' ). + ENDMETHOD. + + METHOD large_unit_all_extras. + assert_price( iv_size = 16 + iv_months = 12 + iv_climate = abap_true + iv_insurance = abap_true + iv_expected = '2524.60' ). + ENDMETHOD. + + METHOD long_term_discount. + assert_price( iv_size = 20 + iv_months = 24 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '3691.00' ). + ENDMETHOD. + + METHOD size_band_change. + assert_price( iv_size = 6 + iv_months = 2 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '157.00' ). + ENDMETHOD. + + METHOD invalid_booking. + assert_price( iv_size = 0 + iv_months = 6 + iv_climate = abap_false + iv_insurance = abap_false + iv_expected = '0.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/seed/storage_pricer.clas.abap b/tasks_gen/train/G1034/seed/storage_pricer.clas.abap new file mode 100644 index 0000000..69d3f28 --- /dev/null +++ b/tasks_gen/train/G1034/seed/storage_pricer.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}storage_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_sqm TYPE i. + TYPES ty_months TYPE i. + + TYPES: + BEGIN OF ty_booking, + size_sqm TYPE ty_sqm, + months TYPE ty_months, + climate TYPE abap_bool, + insurance TYPE abap_bool, + END OF ty_booking. + + METHODS calculate_price + IMPORTING is_booking TYPE ty_booking + RETURNING VALUE(rv_price) TYPE ty_amount. + +ENDCLASS. + + +CLASS {{p}}storage_pricer IMPLEMENTATION. + METHOD calculate_price. + " price of the booking + DATA lv_rate TYPE p LENGTH 8 DECIMALS 2. + DATA lv_rent TYPE p LENGTH 12 DECIMALS 2. + DATA lv_disc TYPE p LENGTH 12 DECIMALS 2. + DATA lv_ins TYPE p LENGTH 12 DECIMALS 2. + DATA lv_sum TYPE p LENGTH 12 DECIMALS 2. + DATA lv_tmp TYPE p LENGTH 12 DECIMALS 2. + + rv_price = 0. + + IF is_booking-size_sqm > 0. + IF is_booking-months > 0. + IF is_booking-size_sqm <= 5. + lv_rate = '14.00'. + ELSE. + IF is_booking-size_sqm <= 15. + lv_rate = '11.50'. + ELSE. + lv_rate = '9.00'. + ENDIF. + ENDIF. + IF is_booking-climate = 'X'. + lv_rate = lv_rate + '4.50'. + ENDIF. + lv_rent = is_booking-size_sqm * is_booking-months * lv_rate. + lv_disc = 0. + IF is_booking-months >= 24. + lv_disc = lv_rent * 15 / 100. + ELSE. + IF is_booking-months >= 12. + lv_disc = lv_rent * 10 / 100. + ELSE. + IF is_booking-months >= 6. + lv_disc = lv_rent * 5 / 100. + ENDIF. + ENDIF. + ENDIF. + lv_ins = 0. + IF is_booking-insurance = 'X'. + lv_ins = is_booking-size_sqm * is_booking-months * '0.90'. + ENDIF. + lv_tmp = lv_rent - lv_disc. + lv_sum = lv_tmp + lv_ins + '19.00'. + rv_price = lv_sum. + ENDIF. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1034/spec.md b/tasks_gen/train/G1034/spec.md new file mode 100644 index 0000000..0807ba2 --- /dev/null +++ b/tasks_gen/train/G1034/spec.md @@ -0,0 +1,46 @@ +# 1. Goal +A self-storage company rents storage units to private and business customers. +The class {{P}}STORAGE_PRICER calculates the price of one storage unit +booking. The class is legacy code. It works, but it is hard to read and hard +to change. Refactor the class. The behavior stays exactly the same. The class +name and the public interface stay the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}STORAGE_PRICER exists in package $TMP and is active. +- A checkout program calls the method CALCULATE_PRICE for each booking. +- The class is the only object of this task. + +# 4. Contract +Keep the class {{P}}STORAGE_PRICER in package $TMP. Keep the class name and +the public interface. The public interface is: +- The class is public, final, and has a public constructor without parameters. +- Public type TY_AMOUNT: packed number, length 8, 2 decimals. +- Public type TY_SQM: integer. +- Public type TY_MONTHS: integer. +- Public type TY_BOOKING with the components SIZE_SQM (TYPE TY_SQM), MONTHS + (TYPE TY_MONTHS), CLIMATE (TYPE ABAP_BOOL) and INSURANCE (TYPE ABAP_BOOL). +- Public method CALCULATE_PRICE with the importing parameter IS_BOOKING + (TYPE TY_BOOKING) and the returning parameter RV_PRICE (TYPE TY_AMOUNT). +Do not add public methods or public components. Do not remove a public method +or a public component. + +# 5. Business rules +The legacy code in {{P}}STORAGE_PRICER contains all business rules. Read the +legacy code. The behavior must stay exactly the same for every input. Do not +add a rule and do not remove a rule. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Methods below 40 statements. No deep nesting. + No magic numbers; use constants. No global variables. No comment that + restates the code. +- Out of scope: no other object. + +# 7. Acceptance +- The class {{P}}STORAGE_PRICER is active and has no syntax error. +- The public interface is unchanged. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1034/task.json b/tasks_gen/train/G1034/task.json new file mode 100644 index 0000000..12c1be6 --- /dev/null +++ b/tasks_gen/train/G1034/task.json @@ -0,0 +1,49 @@ +{ + "id": "G1034", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}STORAGE_PRICER", + "file": "seed/storage_pricer.clas.abap", + "description": "Legacy storage unit rental pricer with deep nesting and magic numbers" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}STORAGE_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_STOR_HIDDEN", + "file": "hidden/t_stor_hidden.clas.abap", + "description": "Hidden tests for the unchanged behavior of {{P}}STORAGE_PRICER" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}STORAGE_PRICER", + "file": "reference/storage_pricer.clas.abap", + "description": "Refactored storage unit rental pricer with the same public interface", + "testclasses_file": "reference/storage_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth", + "magic_numbers" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1035/faulty/m0_photo_pricer.clas.abap b/tasks_gen/train/G1035/faulty/m0_photo_pricer.clas.abap new file mode 100644 index 0000000..7e4882c --- /dev/null +++ b/tasks_gen/train/G1035/faulty/m0_photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes AND is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos < c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km < c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos * c_photo_bulk. + ELSE. + rv_fee = iv_photos * c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/faulty/m1_photo_pricer.clas.abap b/tasks_gen/train/G1035/faulty/m1_photo_pricer.clas.abap new file mode 100644 index 0000000..af9931d --- /dev/null +++ b/tasks_gen/train/G1035/faulty/m1_photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes OR is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos >= c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km < c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos * c_photo_bulk. + ELSE. + rv_fee = iv_photos * c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/faulty/m2_photo_pricer.clas.abap b/tasks_gen/train/G1035/faulty/m2_photo_pricer.clas.abap new file mode 100644 index 0000000..c03e542 --- /dev/null +++ b/tasks_gen/train/G1035/faulty/m2_photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes OR is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos < c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km >= c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos * c_photo_bulk. + ELSE. + rv_fee = iv_photos * c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/faulty/m3_photo_pricer.clas.abap b/tasks_gen/train/G1035/faulty/m3_photo_pricer.clas.abap new file mode 100644 index 0000000..7a4e11c --- /dev/null +++ b/tasks_gen/train/G1035/faulty/m3_photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes OR is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos < c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km < c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos / c_photo_bulk. + ELSE. + rv_fee = iv_photos * c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/faulty/m4_photo_pricer.clas.abap b/tasks_gen/train/G1035/faulty/m4_photo_pricer.clas.abap new file mode 100644 index 0000000..bd3a44b --- /dev/null +++ b/tasks_gen/train/G1035/faulty/m4_photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes OR is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos < c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km < c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos * c_photo_bulk. + ELSE. + rv_fee = iv_photos / c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/generation.json b/tasks_gen/train/G1035/generation.json new file mode 100644 index 0000000..a66555f --- /dev/null +++ b/tasks_gen/train/G1035/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1035", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "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/G1035/hidden/t120_hidden.clas.abap b/tasks_gen/train/G1035/hidden/t120_hidden.clas.abap new file mode 100644 index 0000000..6270d3c --- /dev/null +++ b/tasks_gen/train/G1035/hidden/t120_hidden.clas.abap @@ -0,0 +1,267 @@ +CLASS {{p}}t120_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}photo_pricer. + + METHODS setup. + METHODS studio_quote FOR TESTING. + METHODS outdoor_quote FOR TESTING. + METHODS studio_ignores_distance FOR TESTING. + METHODS rounds_up_minutes FOR TESTING. + METHODS free_persons FOR TESTING. + METHODS crew_fee_large_group FOR TESTING. + METHODS photo_rate_at_50 FOR TESTING. + METHODS rejects_unknown_session FOR TESTING. + METHODS rejects_bad_minutes FOR TESTING. + METHODS rejects_bad_persons FOR TESTING. + METHODS rejects_bad_photos FOR TESTING. + METHODS rejects_outdoor_no_dist FOR TESTING. + + METHODS build_request + IMPORTING iv_session TYPE {{p}}photo_pricer=>ty_session + iv_minutes TYPE i + iv_persons TYPE i + iv_photos TYPE i + iv_distance_km TYPE i + RETURNING VALUE(rs_request) TYPE {{p}}photo_pricer=>ty_request. + + METHODS assert_amount + IMPORTING iv_expected TYPE i + iv_act TYPE {{p}}photo_pricer=>ty_amount + iv_msg TYPE string. +ENDCLASS. + + +CLASS {{p}}t120_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}photo_pricer( ). + ENDMETHOD. + + METHOD build_request. + rs_request = VALUE #( session = iv_session + minutes = iv_minutes + persons = iv_persons + photos = iv_photos + distance_km = iv_distance_km ). + ENDMETHOD. + + METHOD assert_amount. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}photo_pricer=>ty_amount( iv_expected ) + act = iv_act + msg = iv_msg ). + ENDMETHOD. + + METHOD studio_quote. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 90 + iv_persons = 3 + iv_photos = 10 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-ok + act = ls_quote-reason ). + assert_amount( iv_expected = 120 iv_act = ls_quote-time_fee iv_msg = `time fee` ). + assert_amount( iv_expected = 10 iv_act = ls_quote-person_fee iv_msg = `person fee` ). + assert_amount( iv_expected = 50 iv_act = ls_quote-photo_fee iv_msg = `photo fee` ). + assert_amount( iv_expected = 0 iv_act = ls_quote-travel_fee iv_msg = `travel fee` ). + assert_amount( iv_expected = 0 iv_act = ls_quote-crew_fee iv_msg = `crew fee` ). + assert_amount( iv_expected = 180 iv_act = ls_quote-total iv_msg = `total` ). + ENDMETHOD. + + METHOD outdoor_quote. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-outdoor + iv_minutes = 45 + iv_persons = 10 + iv_photos = 50 + iv_distance_km = 20 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-ok + act = ls_quote-reason ). + assert_amount( iv_expected = 60 iv_act = ls_quote-time_fee iv_msg = `time fee` ). + assert_amount( iv_expected = 80 iv_act = ls_quote-person_fee iv_msg = `person fee` ). + assert_amount( iv_expected = 200 iv_act = ls_quote-photo_fee iv_msg = `photo fee` ). + assert_amount( iv_expected = 55 iv_act = ls_quote-travel_fee iv_msg = `travel fee` ). + assert_amount( iv_expected = 40 iv_act = ls_quote-crew_fee iv_msg = `crew fee` ). + assert_amount( iv_expected = 435 iv_act = ls_quote-total iv_msg = `total` ). + ENDMETHOD. + + METHOD studio_ignores_distance. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 1 + iv_photos = 0 + iv_distance_km = 25 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 0 iv_act = ls_quote-travel_fee iv_msg = `travel fee` ). + assert_amount( iv_expected = 40 iv_act = ls_quote-total iv_msg = `total` ). + ENDMETHOD. + + METHOD rounds_up_minutes. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 31 + iv_persons = 1 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 60 iv_act = ls_quote-time_fee iv_msg = `31 minutes` ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 480 + iv_persons = 1 + iv_photos = 0 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 640 iv_act = ls_quote-time_fee iv_msg = `480 minutes` ). + ENDMETHOD. + + METHOD free_persons. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 0 iv_act = ls_quote-person_fee iv_msg = `two persons` ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 5 + iv_photos = 0 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 30 iv_act = ls_quote-person_fee iv_msg = `five persons` ). + ENDMETHOD. + + METHOD crew_fee_large_group. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 8 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 0 iv_act = ls_quote-crew_fee iv_msg = `eight persons` ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 9 + iv_photos = 0 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 70 iv_act = ls_quote-person_fee iv_msg = `nine persons` ). + assert_amount( iv_expected = 40 iv_act = ls_quote-crew_fee iv_msg = `crew fee` ). + assert_amount( iv_expected = 150 iv_act = ls_quote-total iv_msg = `total` ). + ENDMETHOD. + + METHOD photo_rate_at_50. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 1 + iv_photos = 49 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 245 iv_act = ls_quote-photo_fee iv_msg = `49 photos` ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 1 + iv_photos = 50 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + assert_amount( iv_expected = 200 iv_act = ls_quote-photo_fee iv_msg = `50 photos` ). + ENDMETHOD. + + METHOD rejects_unknown_session. + DATA(ls_request) = build_request( iv_session = 'WEDDING' + iv_minutes = 5 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-session + act = ls_quote-reason ). + assert_amount( iv_expected = 0 iv_act = ls_quote-total iv_msg = `total` ). + ENDMETHOD. + + METHOD rejects_bad_minutes. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 29 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-minutes + act = ls_quote-reason ). + assert_amount( iv_expected = 0 iv_act = ls_quote-total iv_msg = `total` ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 481 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-minutes + act = ls_quote-reason ). + ENDMETHOD. + + METHOD rejects_bad_persons. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 60 + iv_persons = 0 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-persons + act = ls_quote-reason ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 60 + iv_persons = 21 + iv_photos = 0 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-persons + act = ls_quote-reason ). + ENDMETHOD. + + METHOD rejects_bad_photos. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 60 + iv_persons = 2 + iv_photos = 201 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-photos + act = ls_quote-reason ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 60 + iv_persons = 2 + iv_photos = -1 + iv_distance_km = 0 ). + ls_quote = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-photos + act = ls_quote-reason ). + ENDMETHOD. + + METHOD rejects_outdoor_no_dist. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-outdoor + iv_minutes = 60 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-distance + act = ls_quote-reason ). + + ls_request = build_request( iv_session = {{p}}photo_pricer=>c_session-outdoor + iv_minutes = 60 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 1 ). + ls_quote = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-ok + act = ls_quote-reason ). + assert_amount( iv_expected = 17 iv_act = ls_quote-travel_fee iv_msg = `travel fee` ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/mutation.json b/tasks_gen/train/G1035/mutation.json new file mode 100644 index 0000000..a07e2cc --- /dev/null +++ b/tasks_gen/train/G1035/mutation.json @@ -0,0 +1,65 @@ +{ + "task": "G1035", + "mutants": [ + { + "object": "{{P}}PHOTO_PRICER", + "mutant": "line 103: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "REJECTS_BAD_MINUTES" + ] + }, + { + "object": "{{P}}PHOTO_PRICER", + "mutant": "line 107: < -> >= (rel)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "CREW_FEE_LARGE_GROUP", + "FREE_PERSONS", + "OUTDOOR_QUOTE", + "PHOTO_RATE_AT_50", + "REJECTS_BAD_PHOTOS", + "REJECTS_OUTDOOR_NO_DIST", + "ROUNDS_UP_MINUTES", + "STUDIO_IGNORES_DISTANCE", + "STUDIO_QUOTE" + ] + }, + { + "object": "{{P}}PHOTO_PRICER", + "mutant": "line 109: < -> >= (rel)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "OUTDOOR_QUOTE", + "REJECTS_OUTDOOR_NO_DIST" + ] + }, + { + "object": "{{P}}PHOTO_PRICER", + "mutant": "line 130: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "OUTDOOR_QUOTE", + "PHOTO_RATE_AT_50" + ] + }, + { + "object": "{{P}}PHOTO_PRICER", + "mutant": "line 132: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "PHOTO_RATE_AT_50", + "STUDIO_QUOTE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1035/reference/photo_pricer.clas.abap b/tasks_gen/train/G1035/reference/photo_pricer.clas.abap new file mode 100644 index 0000000..01a2568 --- /dev/null +++ b/tasks_gen/train/G1035/reference/photo_pricer.clas.abap @@ -0,0 +1,148 @@ +CLASS {{p}}photo_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_session TYPE c LENGTH 7. + TYPES ty_reason TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + TYPES: BEGIN OF ty_request, + session TYPE ty_session, + minutes TYPE i, + persons TYPE i, + photos TYPE i, + distance_km TYPE i, + END OF ty_request. + + TYPES: BEGIN OF ty_quote, + time_fee TYPE ty_amount, + person_fee TYPE ty_amount, + photo_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + crew_fee TYPE ty_amount, + total TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_quote. + + CONSTANTS: + BEGIN OF c_session, + studio TYPE ty_session VALUE 'STUDIO', + outdoor TYPE ty_session VALUE 'OUTDOOR', + END OF c_session, + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + session TYPE ty_reason VALUE 'SESSION', + minutes TYPE ty_reason VALUE 'MINUTES', + persons TYPE ty_reason VALUE 'PERSONS', + photos TYPE ty_reason VALUE 'PHOTOS', + distance TYPE ty_reason VALUE 'DISTANCE', + END OF c_reason. + + METHODS calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. + + PRIVATE SECTION. + CONSTANTS: + c_min_minutes TYPE i VALUE 30, + c_max_minutes TYPE i VALUE 480, + c_min_persons TYPE i VALUE 1, + c_max_persons TYPE i VALUE 20, + c_min_photos TYPE i VALUE 0, + c_max_photos TYPE i VALUE 200, + c_min_distance TYPE i VALUE 1, + c_slot_minutes TYPE i VALUE 15, + c_slot_fee TYPE ty_amount VALUE 20, + c_free_persons TYPE i VALUE 2, + c_person_fee TYPE ty_amount VALUE 10, + c_bulk_limit TYPE i VALUE 50, + c_photo_std TYPE ty_amount VALUE 5, + c_photo_bulk TYPE ty_amount VALUE 4, + c_travel_base TYPE ty_amount VALUE 15, + c_travel_km TYPE ty_amount VALUE 2, + c_crew_limit TYPE i VALUE 8, + c_crew_fee TYPE ty_amount VALUE 40. + + METHODS check_request + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_reason) TYPE ty_reason. + METHODS time_fee + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS person_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS photo_fee + IMPORTING iv_photos TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS travel_fee + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS crew_fee + IMPORTING iv_persons TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}photo_pricer IMPLEMENTATION. + METHOD calculate. + rs_quote-reason = check_request( is_request ). + IF rs_quote-reason <> c_reason-ok. + RETURN. + ENDIF. + rs_quote-time_fee = time_fee( is_request-minutes ). + rs_quote-person_fee = person_fee( is_request-persons ). + rs_quote-photo_fee = photo_fee( is_request-photos ). + rs_quote-travel_fee = travel_fee( is_request ). + rs_quote-crew_fee = crew_fee( is_request-persons ). + rs_quote-total = rs_quote-time_fee + rs_quote-person_fee + rs_quote-photo_fee + + rs_quote-travel_fee + rs_quote-crew_fee. + ENDMETHOD. + + METHOD check_request. + IF is_request-session <> c_session-studio AND is_request-session <> c_session-outdoor. + rv_reason = c_reason-session. + ELSEIF is_request-minutes < c_min_minutes OR is_request-minutes > c_max_minutes. + rv_reason = c_reason-minutes. + ELSEIF is_request-persons < c_min_persons OR is_request-persons > c_max_persons. + rv_reason = c_reason-persons. + ELSEIF is_request-photos < c_min_photos OR is_request-photos > c_max_photos. + rv_reason = c_reason-photos. + ELSEIF is_request-session = c_session-outdoor AND is_request-distance_km < c_min_distance. + rv_reason = c_reason-distance. + ELSE. + rv_reason = c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD time_fee. + DATA(lv_slots) = ( iv_minutes + c_slot_minutes - 1 ) DIV c_slot_minutes. + rv_fee = lv_slots * c_slot_fee. + ENDMETHOD. + + METHOD person_fee. + DATA(lv_extra) = iv_persons - c_free_persons. + IF lv_extra > 0. + rv_fee = lv_extra * c_person_fee. + ENDIF. + ENDMETHOD. + + METHOD photo_fee. + IF iv_photos >= c_bulk_limit. + rv_fee = iv_photos * c_photo_bulk. + ELSE. + rv_fee = iv_photos * c_photo_std. + ENDIF. + ENDMETHOD. + + METHOD travel_fee. + IF is_request-session <> c_session-outdoor. + RETURN. + ENDIF. + rv_fee = c_travel_base + is_request-distance_km * c_travel_km. + ENDMETHOD. + + METHOD crew_fee. + IF iv_persons > c_crew_limit. + rv_fee = c_crew_fee. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/reference/photo_pricer.testclasses.abap b/tasks_gen/train/G1035/reference/photo_pricer.testclasses.abap new file mode 100644 index 0000000..c950e74 --- /dev/null +++ b/tasks_gen/train/G1035/reference/photo_pricer.testclasses.abap @@ -0,0 +1,121 @@ +CLASS ltc_photo_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}photo_pricer. + + METHODS setup. + METHODS studio_quote FOR TESTING. + METHODS outdoor_quote FOR TESTING. + METHODS studio_ignores_distance FOR TESTING. + METHODS rounds_up_minutes FOR TESTING. + METHODS rejects_unknown_session FOR TESTING. + METHODS rejects_bad_minutes FOR TESTING. + METHODS rejects_outdoor_no_distance FOR TESTING. + + METHODS build_request + IMPORTING iv_session TYPE {{p}}photo_pricer=>ty_session + iv_minutes TYPE i + iv_persons TYPE i + iv_photos TYPE i + iv_distance_km TYPE i + RETURNING VALUE(rs_request) TYPE {{p}}photo_pricer=>ty_request. +ENDCLASS. + + +CLASS ltc_photo_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}photo_pricer( ). + ENDMETHOD. + + METHOD build_request. + rs_request = VALUE #( session = iv_session + minutes = iv_minutes + persons = iv_persons + photos = iv_photos + distance_km = iv_distance_km ). + ENDMETHOD. + + METHOD studio_quote. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 90 + iv_persons = 3 + iv_photos = 10 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-ok + act = ls_quote-reason ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}photo_pricer=>ty_amount( 180 ) + act = ls_quote-total ). + ENDMETHOD. + + METHOD outdoor_quote. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-outdoor + iv_minutes = 45 + iv_persons = 10 + iv_photos = 50 + iv_distance_km = 20 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-ok + act = ls_quote-reason ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}photo_pricer=>ty_amount( 435 ) + act = ls_quote-total ). + ENDMETHOD. + + METHOD studio_ignores_distance. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 30 + iv_persons = 1 + iv_photos = 0 + iv_distance_km = 25 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}photo_pricer=>ty_amount( 0 ) + act = ls_quote-travel_fee ). + ENDMETHOD. + + METHOD rounds_up_minutes. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 31 + iv_persons = 1 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}photo_pricer=>ty_amount( 60 ) + act = ls_quote-time_fee ). + ENDMETHOD. + + METHOD rejects_unknown_session. + DATA(ls_request) = build_request( iv_session = 'WEDDING' + iv_minutes = 60 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-session + act = ls_quote-reason ). + ENDMETHOD. + + METHOD rejects_bad_minutes. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-studio + iv_minutes = 29 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-minutes + act = ls_quote-reason ). + ENDMETHOD. + + METHOD rejects_outdoor_no_distance. + DATA(ls_request) = build_request( iv_session = {{p}}photo_pricer=>c_session-outdoor + iv_minutes = 60 + iv_persons = 2 + iv_photos = 0 + iv_distance_km = 0 ). + DATA(ls_quote) = mo_cut->calculate( ls_request ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}photo_pricer=>c_reason-distance + act = ls_quote-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1035/spec.md b/tasks_gen/train/G1035/spec.md new file mode 100644 index 0000000..b05e369 --- /dev/null +++ b/tasks_gen/train/G1035/spec.md @@ -0,0 +1,80 @@ +# 1. Goal + +A photo studio prices a photo shoot. A booking program calls the calculation +before it shows the quote to the customer. The program needs the fee parts and +the total fee. + +# 2. Open questions + +None. + +# 3. Context + +- The class {{P}}PHOTO_PRICER does not exist. Create it in package $TMP. +- The booking program uses the public types, the public constants and the + public method of the class. It does not use other parts of the class. + +# 4. Contract + +- Create the class {{P}}PHOTO_PRICER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - ty_session: type c, length 7. + - ty_reason: type c, length 8. + - ty_amount: type p, length 8, 2 decimal places. + - ty_request: a structure with the components session (ty_session), + minutes (i), persons (i), photos (i) and distance_km (i). + - ty_quote: a structure with the components time_fee (ty_amount), + person_fee (ty_amount), photo_fee (ty_amount), travel_fee (ty_amount), + crew_fee (ty_amount), total (ty_amount) and reason (ty_reason). +- The class has these public constants: + - c_session with the components studio = 'STUDIO' and outdoor = 'OUTDOOR'. + Both components have the type ty_session. + - c_reason with the components ok = 'OK', session = 'SESSION', + minutes = 'MINUTES', persons = 'PERSONS', photos = 'PHOTOS' and + distance = 'DISTANCE'. All components have the type ty_reason. +- The class has one public method: + calculate + IMPORTING is_request TYPE ty_request + RETURNING VALUE(rs_quote) TYPE ty_quote. +- Do not add other public methods. + +# 5. Business rules + +First check the input. Apply the checks in this sequence. If a check fails, set +rs_quote-reason to the reason of that check, keep all fee components and the +total at 0, and stop. + +1. is_request-session must be STUDIO or OUTDOOR. Else reason = SESSION. +2. is_request-minutes must be 30 or more and 480 or less. Else reason = MINUTES. +3. is_request-persons must be 1 or more and 20 or less. Else reason = PERSONS. +4. is_request-photos must be 0 or more and 200 or less. Else reason = PHOTOS. +5. If the session is OUTDOOR, is_request-distance_km must be 1 or more. Else + reason = DISTANCE. For a STUDIO session, ignore distance_km. + +If all checks pass, set reason = OK and calculate the fees. + +- time_fee: each started 15 minutes of the shoot costs 20.00. Example: 30 + minutes cost 40.00. 31 minutes cost 60.00. 480 minutes cost 640.00. +- person_fee: the first 2 persons are free. Each other person costs 10.00. +- photo_fee: if photos is less than 50, each photo costs 5.00. If photos is 50 + or more, each photo costs 4.00. +- travel_fee: for a STUDIO session, 0.00. For an OUTDOOR session, 15.00 plus + 2.00 for each kilometre of distance_km. +- crew_fee: if persons is more than 8, the studio sends a second photographer. + The crew_fee is then 40.00. Else the crew_fee is 0.00. +- total: the sum of time_fee, person_fee, photo_fee, travel_fee and crew_fee. + +# 6. Constraints + +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. Each method has less than 40 statements. No + global variables. No comment that restates the code. +- Out of scope: database access, ALV lists, CDS views, function modules, + reports and dynpros. + +# 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/G1035/task.json b/tasks_gen/train/G1035/task.json new file mode 100644 index 0000000..4dff978 --- /dev/null +++ b/tasks_gen/train/G1035/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1035", + "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}}PHOTO_PRICER" + } + ], + "out_of_scope": [ + "database access", + "ALV lists", + "CDS views", + "function modules", + "reports", + "dynpros" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T120_HIDDEN", + "file": "hidden/t120_hidden.clas.abap", + "description": "T120 hidden tests for the photo shoot pricer" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PHOTO_PRICER", + "file": "reference/photo_pricer.clas.abap", + "description": "Photo shoot pricer", + "testclasses_file": "reference/photo_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1036/faulty/m0_ride_booking.clas.abap b/tasks_gen/train/G1036/faulty/m0_ride_booking.clas.abap new file mode 100644 index 0000000..998680c --- /dev/null +++ b/tasks_gen/train/G1036/faulty/m0_ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) - level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders < 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 20 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/faulty/m1_ride_booking.clas.abap b/tasks_gen/train/G1036/faulty/m1_ride_booking.clas.abap new file mode 100644 index 0000000..9584d91 --- /dev/null +++ b/tasks_gen/train/G1036/faulty/m1_ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) + level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + OR iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders < 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 20 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/faulty/m2_ride_booking.clas.abap b/tasks_gen/train/G1036/faulty/m2_ride_booking.clas.abap new file mode 100644 index 0000000..7e67cae --- /dev/null +++ b/tasks_gen/train/G1036/faulty/m2_ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) + level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders >= 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 20 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/faulty/m3_ride_booking.clas.abap b/tasks_gen/train/G1036/faulty/m3_ride_booking.clas.abap new file mode 100644 index 0000000..09caf44 --- /dev/null +++ b/tasks_gen/train/G1036/faulty/m3_ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) + level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders < 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 20 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/faulty/m4_ride_booking.clas.abap b/tasks_gen/train/G1036/faulty/m4_ride_booking.clas.abap new file mode 100644 index 0000000..5ddd0af --- /dev/null +++ b/tasks_gen/train/G1036/faulty/m4_ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) + level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders < 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 21 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/generation.json b/tasks_gen/train/G1036/generation.json new file mode 100644 index 0000000..68b8de5 --- /dev/null +++ b/tasks_gen/train/G1036/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1036", + "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/G1036/hidden/ride_hidden.clas.abap b/tasks_gen/train/G1036/hidden/ride_hidden.clas.abap new file mode 100644 index 0000000..f6de08b --- /dev/null +++ b/tasks_gen/train/G1036/hidden/ride_hidden.clas.abap @@ -0,0 +1,226 @@ +CLASS {{p}}ride_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_ride_booking. + + METHODS setup. + METHODS exp_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS price_dressage_basic FOR TESTING. + METHODS price_jumping_medium FOR TESTING. + METHODS price_western_group3 FOR TESTING. + METHODS price_vaulting_group6 FOR TESTING. + METHODS price_dressage_group5 FOR TESTING. + METHODS name_empty_raises FOR TESTING. + METHODS discipline_unknown_raises FOR TESTING. + METHODS level_unknown_raises FOR TESTING. + METHODS minutes_invalid_raises FOR TESTING. + METHODS riders_invalid_raises FOR TESTING. + METHODS jumping_level_raises FOR TESTING. + METHODS vaulting_group_raises FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ride_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}ride_booking( ). + ENDMETHOD. + + METHOD exp_amount. + DATA lv_amount TYPE {{p}}if_ride_booking=>ty_amount. + lv_amount = iv_cents. + rv_amount = lv_amount / 100. + ENDMETHOD. + + METHOD price_dressage_basic. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 2000 ) + act = mo_cut->book( iv_rider_name = `Anna` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD price_jumping_medium. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 5800 ) + act = mo_cut->book( iv_rider_name = `Berta` + iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + iv_level = {{p}}if_ride_booking=>c_level-medium + iv_minutes = 60 + iv_riders = 2 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD price_western_group3. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 4800 ) + act = mo_cut->book( iv_rider_name = `Carl` + iv_discipline = {{p}}if_ride_booking=>c_discipline-western + iv_level = {{p}}if_ride_booking=>c_level-light + iv_minutes = 90 + iv_riders = 3 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD price_vaulting_group6. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 5040 ) + act = mo_cut->book( iv_rider_name = `Dora` + iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + iv_level = {{p}}if_ride_booking=>c_level-advanced + iv_minutes = 120 + iv_riders = 6 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD price_dressage_group5. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 4320 ) + act = mo_cut->book( iv_rider_name = `Emil` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-medium + iv_minutes = 90 + iv_riders = 5 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD name_empty_raises. + TRY. + mo_cut->book( iv_rider_name = `` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-name + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD discipline_unknown_raises. + TRY. + mo_cut->book( iv_rider_name = `Frida` + iv_discipline = 'XXXX' + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-discipline + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD level_unknown_raises. + TRY. + mo_cut->book( iv_rider_name = `Gustav` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = 'X' + iv_minutes = 30 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-level + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD minutes_invalid_raises. + TRY. + mo_cut->book( iv_rider_name = `Hanna` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 45 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-minutes + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD riders_invalid_raises. + TRY. + mo_cut->book( iv_rider_name = `Ida` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 7 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-riders + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD jumping_level_raises. + TRY. + mo_cut->book( iv_rider_name = `Jonas` + iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 60 + iv_riders = 2 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input. + cl_abap_unit_assert=>fail( 'wrong exception' ). + CATCH {{p}}cx_ride_limit INTO DATA(lx_limit). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-show_level + act = lx_limit->reason ). + ENDTRY. + ENDMETHOD. + + METHOD vaulting_group_raises. + TRY. + mo_cut->book( iv_rider_name = `Klara` + iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 60 + iv_riders = 2 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input. + cl_abap_unit_assert=>fail( 'wrong exception' ). + CATCH {{p}}cx_ride_limit INTO DATA(lx_limit). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-volt_group + act = lx_limit->reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/mutation.json b/tasks_gen/train/G1036/mutation.json new file mode 100644 index 0000000..a70a91b --- /dev/null +++ b/tasks_gen/train/G1036/mutation.json @@ -0,0 +1,75 @@ +{ + "task": "G1036", + "mutants": [ + { + "object": "{{P}}RIDE_BOOKING", + "mutant": "line 56: + -> - (arith)", + "status": "killed", + "hidden": "8/12", + "failed_tests": [ + "PRICE_DRESSAGE_GROUP5", + "PRICE_JUMPING_MEDIUM", + "PRICE_VAULTING_GROUP6", + "PRICE_WESTERN_GROUP3" + ] + }, + { + "object": "{{P}}RIDE_BOOKING", + "mutant": "line 76: AND -> OR (logic)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "JUMPING_LEVEL_RAISES", + "MINUTES_INVALID_RAISES", + "PRICE_DRESSAGE_BASIC", + "PRICE_DRESSAGE_GROUP5", + "PRICE_JUMPING_MEDIUM", + "PRICE_VAULTING_GROUP6", + "PRICE_WESTERN_GROUP3", + "RIDERS_INVALID_RAISES", + "VAULTING_GROUP_RAISES" + ] + }, + { + "object": "{{P}}RIDE_BOOKING", + "mutant": "line 87: < -> >= (rel)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "JUMPING_LEVEL_RAISES", + "PRICE_DRESSAGE_BASIC", + "PRICE_DRESSAGE_GROUP5", + "PRICE_JUMPING_MEDIUM", + "PRICE_VAULTING_GROUP6", + "PRICE_WESTERN_GROUP3", + "VAULTING_GROUP_RAISES" + ] + }, + { + "object": "{{P}}RIDE_BOOKING", + "mutant": "line 94: = -> <> (eq)", + "status": "killed", + "hidden": "8/12", + "failed_tests": [ + "JUMPING_LEVEL_RAISES", + "PRICE_DRESSAGE_BASIC", + "PRICE_WESTERN_GROUP3", + "VAULTING_GROUP_RAISES" + ] + }, + { + "object": "{{P}}RIDE_BOOKING", + "mutant": "line 110: 20 -> 21 (const)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "PRICE_DRESSAGE_BASIC", + "PRICE_DRESSAGE_GROUP5" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1036/reference/ride_booking.clas.abap b/tasks_gen/train/G1036/reference/ride_booking.clas.abap new file mode 100644 index 0000000..9912c06 --- /dev/null +++ b/tasks_gen/train/G1036/reference/ride_booking.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}ride_booking DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_ride_booking. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_rate, + discipline TYPE {{p}}if_ride_booking=>ty_discipline, + base TYPE {{p}}if_ride_booking=>ty_amount, + END OF ty_rate, + tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY discipline. + + METHODS check_input + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_minutes TYPE i + iv_riders TYPE i + RAISING {{p}}cx_ride_input. + + METHODS check_limits + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + iv_level TYPE {{p}}if_ride_booking=>ty_level + iv_riders TYPE i + RAISING {{p}}cx_ride_limit. + + METHODS base_rate + IMPORTING iv_discipline TYPE {{p}}if_ride_booking=>ty_discipline + RETURNING VALUE(rv_rate) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS level_surcharge + IMPORTING iv_level TYPE {{p}}if_ride_booking=>ty_level + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS discount + IMPORTING iv_riders TYPE i + RETURNING VALUE(rv_percent) TYPE i. +ENDCLASS. + + +CLASS {{p}}ride_booking IMPLEMENTATION. + METHOD {{p}}if_ride_booking~book. + check_input( iv_rider_name = iv_rider_name + iv_discipline = iv_discipline + iv_level = iv_level + iv_minutes = iv_minutes + iv_riders = iv_riders ). + check_limits( iv_discipline = iv_discipline + iv_level = iv_level + iv_riders = iv_riders ). + + DATA lv_periods TYPE i. + DATA lv_gross TYPE {{p}}if_ride_booking=>ty_amount. + + lv_periods = iv_minutes DIV 30. + lv_gross = ( base_rate( iv_discipline ) + level_surcharge( iv_level ) ) * lv_periods. + rv_price = lv_gross * ( 100 - discount( iv_riders ) ) / 100. + ENDMETHOD. + + METHOD check_input. + IF iv_rider_name IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-name. + ENDIF. + + IF iv_discipline <> {{p}}if_ride_booking=>c_discipline-dressage + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-jumping + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-western + AND iv_discipline <> {{p}}if_ride_booking=>c_discipline-vaulting. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-discipline. + ENDIF. + + IF iv_level <> {{p}}if_ride_booking=>c_level-basic + AND iv_level <> {{p}}if_ride_booking=>c_level-light + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-level. + ENDIF. + + IF iv_minutes < 30 OR iv_minutes > 120 OR iv_minutes MOD 30 <> 0. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-minutes. + ENDIF. + + IF iv_riders < 1 OR iv_riders > 6. + RAISE EXCEPTION TYPE {{p}}cx_ride_input + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-riders. + ENDIF. + ENDMETHOD. + + METHOD check_limits. + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + AND iv_level <> {{p}}if_ride_booking=>c_level-medium + AND iv_level <> {{p}}if_ride_booking=>c_level-advanced. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-show_level. + ENDIF. + + IF iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + AND iv_riders < 3. + RAISE EXCEPTION TYPE {{p}}cx_ride_limit + EXPORTING iv_reason = {{p}}if_ride_booking=>c_reason-volt_group. + ENDIF. + ENDMETHOD. + + METHOD base_rate. + DATA(lt_rate) = VALUE tt_rate( + ( discipline = {{p}}if_ride_booking=>c_discipline-dressage base = 20 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-jumping base = 25 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-western base = 18 ) + ( discipline = {{p}}if_ride_booking=>c_discipline-vaulting base = 15 ) ). + rv_rate = lt_rate[ discipline = iv_discipline ]-base. + ENDMETHOD. + + METHOD level_surcharge. + CASE iv_level. + WHEN {{p}}if_ride_booking=>c_level-light. + rv_amount = 2. + WHEN {{p}}if_ride_booking=>c_level-medium. + rv_amount = 4. + WHEN {{p}}if_ride_booking=>c_level-advanced. + rv_amount = 6. + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD discount. + CASE iv_riders. + WHEN 1 OR 2. + rv_percent = 0. + WHEN 3 OR 4. + rv_percent = 20. + WHEN OTHERS. + rv_percent = 40. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/reference/ride_booking.testclasses.abap b/tasks_gen/train/G1036/reference/ride_booking.testclasses.abap new file mode 100644 index 0000000..37ed686 --- /dev/null +++ b/tasks_gen/train/G1036/reference/ride_booking.testclasses.abap @@ -0,0 +1,143 @@ +CLASS ltc_ride_booking DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_ride_booking. + + METHODS setup. + METHODS exp_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_ride_booking=>ty_amount. + + METHODS price_basic_lesson FOR TESTING. + METHODS price_group_discount FOR TESTING. + METHODS empty_name_raises FOR TESTING. + METHODS unknown_discipline_raises FOR TESTING. + METHODS invalid_minutes_raises FOR TESTING. + METHODS jumping_needs_level FOR TESTING. + METHODS vaulting_needs_group FOR TESTING. +ENDCLASS. + + +CLASS ltc_ride_booking IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}ride_booking( ). + ENDMETHOD. + + METHOD exp_amount. + DATA lv_amount TYPE {{p}}if_ride_booking=>ty_amount. + lv_amount = iv_cents. + rv_amount = lv_amount / 100. + ENDMETHOD. + + METHOD price_basic_lesson. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 2000 ) + act = mo_cut->book( iv_rider_name = `Anna` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD price_group_discount. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = exp_amount( 5040 ) + act = mo_cut->book( iv_rider_name = `Ben` + iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + iv_level = {{p}}if_ride_booking=>c_level-advanced + iv_minutes = 120 + iv_riders = 6 ) ). + CATCH {{p}}cx_ride_booking. + cl_abap_unit_assert=>fail( 'exception not expected' ). + ENDTRY. + ENDMETHOD. + + METHOD empty_name_raises. + TRY. + mo_cut->book( iv_rider_name = `` + iv_discipline = {{p}}if_ride_booking=>c_discipline-dressage + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-name + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD unknown_discipline_raises. + TRY. + mo_cut->book( iv_rider_name = `Cara` + iv_discipline = 'XXXX' + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 30 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-discipline + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD invalid_minutes_raises. + TRY. + mo_cut->book( iv_rider_name = `Dana` + iv_discipline = {{p}}if_ride_booking=>c_discipline-western + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 20 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input INTO DATA(lx_input). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-minutes + act = lx_input->reason ). + CATCH {{p}}cx_ride_limit. + cl_abap_unit_assert=>fail( 'wrong exception' ). + ENDTRY. + ENDMETHOD. + + METHOD jumping_needs_level. + TRY. + mo_cut->book( iv_rider_name = `Erik` + iv_discipline = {{p}}if_ride_booking=>c_discipline-jumping + iv_level = {{p}}if_ride_booking=>c_level-light + iv_minutes = 60 + iv_riders = 1 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input. + cl_abap_unit_assert=>fail( 'wrong exception' ). + CATCH {{p}}cx_ride_limit INTO DATA(lx_limit). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-show_level + act = lx_limit->reason ). + ENDTRY. + ENDMETHOD. + + METHOD vaulting_needs_group. + TRY. + mo_cut->book( iv_rider_name = `Fiona` + iv_discipline = {{p}}if_ride_booking=>c_discipline-vaulting + iv_level = {{p}}if_ride_booking=>c_level-basic + iv_minutes = 60 + iv_riders = 2 ). + cl_abap_unit_assert=>fail( 'exception expected' ). + CATCH {{p}}cx_ride_input. + cl_abap_unit_assert=>fail( 'wrong exception' ). + CATCH {{p}}cx_ride_limit INTO DATA(lx_limit). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ride_booking=>c_reason-volt_group + act = lx_limit->reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/seed/cx_ride_booking.clas.abap b/tasks_gen/train/G1036/seed/cx_ride_booking.clas.abap new file mode 100644 index 0000000..bc8dba9 --- /dev/null +++ b/tasks_gen/train/G1036/seed/cx_ride_booking.clas.abap @@ -0,0 +1,19 @@ +CLASS {{p}}cx_ride_booking DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + DATA reason TYPE ty_reason READ-ONLY. + + METHODS constructor + IMPORTING iv_reason TYPE ty_reason. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_ride_booking IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1036/seed/cx_ride_input.clas.abap b/tasks_gen/train/G1036/seed/cx_ride_input.clas.abap new file mode 100644 index 0000000..9d49bc5 --- /dev/null +++ b/tasks_gen/train/G1036/seed/cx_ride_input.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_ride_input DEFINITION PUBLIC INHERITING FROM {{p}}cx_ride_booking FINAL CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_ride_input IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1036/seed/cx_ride_limit.clas.abap b/tasks_gen/train/G1036/seed/cx_ride_limit.clas.abap new file mode 100644 index 0000000..52fb3bc --- /dev/null +++ b/tasks_gen/train/G1036/seed/cx_ride_limit.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_ride_limit DEFINITION PUBLIC INHERITING FROM {{p}}cx_ride_booking FINAL CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_ride_limit IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1036/seed/if_ride_booking.intf.abap b/tasks_gen/train/G1036/seed/if_ride_booking.intf.abap new file mode 100644 index 0000000..66dc01e --- /dev/null +++ b/tasks_gen/train/G1036/seed/if_ride_booking.intf.abap @@ -0,0 +1,43 @@ +INTERFACE {{p}}if_ride_booking PUBLIC. + TYPES ty_discipline TYPE c LENGTH 4. + TYPES ty_level TYPE c LENGTH 1. + TYPES ty_reason TYPE {{p}}cx_ride_booking=>ty_reason. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_discipline, + dressage TYPE ty_discipline VALUE 'DRES', + jumping TYPE ty_discipline VALUE 'SHOW', + western TYPE ty_discipline VALUE 'WEST', + vaulting TYPE ty_discipline VALUE 'VOLT', + END OF c_discipline. + + CONSTANTS: + BEGIN OF c_level, + basic TYPE ty_level VALUE 'B', + light TYPE ty_level VALUE 'L', + medium TYPE ty_level VALUE 'M', + advanced TYPE ty_level VALUE 'S', + END OF c_level. + + CONSTANTS: + BEGIN OF c_reason, + name TYPE ty_reason VALUE 'NAME', + discipline TYPE ty_reason VALUE 'DISCIPLINE', + level TYPE ty_reason VALUE 'LEVEL', + minutes TYPE ty_reason VALUE 'MINUTES', + riders TYPE ty_reason VALUE 'RIDERS', + show_level TYPE ty_reason VALUE 'SHOW_LEVEL', + volt_group TYPE ty_reason VALUE 'VOLT_GROUP', + END OF c_reason. + + METHODS book + IMPORTING iv_rider_name TYPE string + iv_discipline TYPE ty_discipline + iv_level TYPE ty_level + iv_minutes TYPE i + iv_riders TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount + RAISING {{p}}cx_ride_input + {{p}}cx_ride_limit. +ENDINTERFACE. diff --git a/tasks_gen/train/G1036/spec.md b/tasks_gen/train/G1036/spec.md new file mode 100644 index 0000000..3510178 --- /dev/null +++ b/tasks_gen/train/G1036/spec.md @@ -0,0 +1,79 @@ +# 1. Goal +A riding stable sells riding lessons. A booking program calls the class +{{P}}RIDE_BOOKING for each booking request. The class checks the request and +returns the price that one rider pays. The class stops the booking with a +class-based exception if the request is not correct. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_RIDE_BOOKING is in package $TMP. It is active. +- The interface has these types: + TY_DISCIPLINE (character 4), TY_LEVEL (character 1), TY_REASON + (character 10), TY_AMOUNT (packed number, length 8, 2 decimal places). +- The interface has these constants: + C_DISCIPLINE: DRESSAGE 'DRES', JUMPING 'SHOW', WESTERN 'WEST', + VAULTING 'VOLT'. + C_LEVEL: BASIC 'B', LIGHT 'L', MEDIUM 'M', ADVANCED 'S'. + C_REASON: NAME 'NAME', DISCIPLINE 'DISCIPLINE', LEVEL 'LEVEL', + MINUTES 'MINUTES', RIDERS 'RIDERS', SHOW_LEVEL 'SHOW_LEVEL', + VOLT_GROUP 'VOLT_GROUP'. +- The interface has one method: + BOOK IMPORTING iv_rider_name TYPE string + iv_discipline TYPE ty_discipline + iv_level TYPE ty_level + iv_minutes TYPE i + iv_riders TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount + RAISING {{P}}CX_RIDE_INPUT + {{P}}CX_RIDE_LIMIT. +- The exception classes {{P}}CX_RIDE_BOOKING, {{P}}CX_RIDE_INPUT and + {{P}}CX_RIDE_LIMIT are in package $TMP. They are active. +- {{P}}CX_RIDE_BOOKING inherits from CX_STATIC_CHECK. It has the type + TY_REASON (character 10), the read-only attribute REASON of type TY_REASON + and the constructor with the importing parameter IV_REASON of type TY_REASON. +- {{P}}CX_RIDE_INPUT and {{P}}CX_RIDE_LIMIT inherit from + {{P}}CX_RIDE_BOOKING. They add nothing. + +# 4. Contract +- Create the class {{P}}RIDE_BOOKING in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_RIDE_BOOKING. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Raise the exception of the first rule that +fails. +1. If the rider name is empty, raise {{P}}CX_RIDE_INPUT with the reason NAME. +2. The discipline must be DRES, SHOW, WEST or VOLT. Else raise + {{P}}CX_RIDE_INPUT with the reason DISCIPLINE. +3. The level must be B, L, M or S. Else raise {{P}}CX_RIDE_INPUT with the + reason LEVEL. +4. The number of minutes must be 30, 60, 90 or 120. Else raise + {{P}}CX_RIDE_INPUT with the reason MINUTES. +5. The number of riders must be between 1 and 6. Else raise + {{P}}CX_RIDE_INPUT with the reason RIDERS. +6. Show jumping (SHOW) is only for the levels M and S. Else raise + {{P}}CX_RIDE_LIMIT with the reason SHOW_LEVEL. +7. Vaulting (VOLT) is only for a group lesson with 3 or more riders. Else + raise {{P}}CX_RIDE_LIMIT with the reason VOLT_GROUP. +8. Calculate the price that one rider pays: + a. The base rate for 30 minutes: DRES 20, SHOW 25, WEST 18, VOLT 15. + b. The level surcharge for 30 minutes: B 0, L 2, M 4, S 6. + c. The gross price = (base rate + level surcharge) * (minutes / 30). + d. The group discount: 1 or 2 riders 0 percent, 3 or 4 riders 20 percent, + 5 or 6 riders 40 percent. + e. The price = gross price * (100 - group discount) / 100. + +# 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_RIDE_BOOKING, {{P}}CX_RIDE_BOOKING, + {{P}}CX_RIDE_INPUT and {{P}}CX_RIDE_LIMIT. + +# 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/G1036/task.json b/tasks_gen/train/G1036/task.json new file mode 100644 index 0000000..d53cada --- /dev/null +++ b/tasks_gen/train/G1036/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1036", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 80, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}CX_RIDE_BOOKING", + "file": "seed/cx_ride_booking.clas.abap", + "description": "Base exception with a reason code" + }, + { + "type": "CLAS", + "name": "{{P}}CX_RIDE_INPUT", + "file": "seed/cx_ride_input.clas.abap", + "description": "Exception for wrong input data" + }, + { + "type": "CLAS", + "name": "{{P}}CX_RIDE_LIMIT", + "file": "seed/cx_ride_limit.clas.abap", + "description": "Exception for a business limit" + }, + { + "type": "INTF", + "name": "{{P}}IF_RIDE_BOOKING", + "file": "seed/if_ride_booking.intf.abap", + "description": "Riding lesson booking contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}RIDE_BOOKING", + "implements": "{{P}}IF_RIDE_BOOKING" + } + ], + "out_of_scope": [ + "{{P}}IF_RIDE_BOOKING", + "{{P}}CX_RIDE_BOOKING", + "{{P}}CX_RIDE_INPUT", + "{{P}}CX_RIDE_LIMIT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}RIDE_HIDDEN", + "file": "hidden/ride_hidden.clas.abap", + "description": "T41 hidden tests for the riding lesson booking" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}RIDE_BOOKING", + "file": "reference/ride_booking.clas.abap", + "description": "Riding lesson booking with class-based exceptions", + "testclasses_file": "reference/ride_booking.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1037/faulty/m0_piano_pricer.clas.abap b/tasks_gen/train/G1037/faulty/m0_piano_pricer.clas.abap new file mode 100644 index 0000000..0299e23 --- /dev/null +++ b/tasks_gen/train/G1037/faulty/m0_piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee - rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes <= 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes * is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km <= 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km * {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/faulty/m1_piano_pricer.clas.abap b/tasks_gen/train/G1037/faulty/m1_piano_pricer.clas.abap new file mode 100644 index 0000000..114558b --- /dev/null +++ b/tasks_gen/train/G1037/faulty/m1_piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee + rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes > 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes * is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km <= 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km * {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/faulty/m2_piano_pricer.clas.abap b/tasks_gen/train/G1037/faulty/m2_piano_pricer.clas.abap new file mode 100644 index 0000000..5980795 --- /dev/null +++ b/tasks_gen/train/G1037/faulty/m2_piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee + rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes <= 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes / is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km <= 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km * {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/faulty/m3_piano_pricer.clas.abap b/tasks_gen/train/G1037/faulty/m3_piano_pricer.clas.abap new file mode 100644 index 0000000..18539ce --- /dev/null +++ b/tasks_gen/train/G1037/faulty/m3_piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee + rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes <= 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes * is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km > 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km * {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/faulty/m4_piano_pricer.clas.abap b/tasks_gen/train/G1037/faulty/m4_piano_pricer.clas.abap new file mode 100644 index 0000000..b6782ee --- /dev/null +++ b/tasks_gen/train/G1037/faulty/m4_piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee + rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes <= 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes * is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km <= 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km / {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/generation.json b/tasks_gen/train/G1037/generation.json new file mode 100644 index 0000000..7a7c34e --- /dev/null +++ b/tasks_gen/train/G1037/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1037", + "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/G1037/hidden/t42_hidden.clas.abap b/tasks_gen/train/G1037/hidden/t42_hidden.clas.abap new file mode 100644 index 0000000..a77a454 --- /dev/null +++ b/tasks_gen/train/G1037/hidden/t42_hidden.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}t42_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}piano_pricer. + + METHODS setup. + METHODS assert_charge + IMPORTING is_charge TYPE {{p}}piano_pricer=>ty_charge + iv_reason TYPE {{p}}piano_pricer=>ty_reason + iv_base TYPE {{p}}piano_pricer=>ty_amount + iv_travel TYPE {{p}}piano_pricer=>ty_amount + iv_extra TYPE {{p}}piano_pricer=>ty_amount + iv_total TYPE {{p}}piano_pricer=>ty_amount. + + METHODS upright_basic FOR TESTING. + METHODS grand_pitch_raise FOR TESTING. + METHODS travel_beyond_free FOR TESTING. + METHODS digital_piano FOR TESTING. + METHODS studio_piano FOR TESTING. + METHODS concert_grand FOR TESTING. + METHODS unknown_type FOR TESTING. + METHODS negative_distance FOR TESTING. + METHODS negative_pitch FOR TESTING. + METHODS unknown_wins FOR TESTING. + METHODS exact_limits FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t42_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}piano_pricer( ). + ENDMETHOD. + + METHOD assert_charge. + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_charge-reason msg = 'reason' ). + cl_abap_unit_assert=>assert_equals( exp = iv_base act = is_charge-base_fee msg = 'base_fee' ). + cl_abap_unit_assert=>assert_equals( exp = iv_travel act = is_charge-travel_fee msg = 'travel_fee' ). + cl_abap_unit_assert=>assert_equals( exp = iv_extra act = is_charge-extra_fee msg = 'extra_fee' ). + cl_abap_unit_assert=>assert_equals( exp = iv_total act = is_charge-total_fee msg = 'total_fee' ). + ENDMETHOD. + + METHOD upright_basic. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 5 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '89.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '89.00' ). + ENDMETHOD. + + METHOD grand_pitch_raise. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'G' + iv_distance_km = 0 + iv_pitch_cents = 40 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '129.00' iv_travel = '0.00' + iv_extra = '15.00' iv_total = '144.00' ). + ENDMETHOD. + + METHOD travel_beyond_free. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 25 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '89.00' iv_travel = '12.00' + iv_extra = '0.00' iv_total = '101.00' ). + ENDMETHOD. + + METHOD digital_piano. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'D' + iv_distance_km = 10 + iv_pitch_cents = 10 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '69.00' iv_travel = '0.00' + iv_extra = '10.00' iv_total = '79.00' ). + ENDMETHOD. + + METHOD studio_piano. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'S' + iv_distance_km = 12 + iv_pitch_cents = 20 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '99.00' iv_travel = '1.60' + iv_extra = '6.50' iv_total = '107.10' ). + ENDMETHOD. + + METHOD concert_grand. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'C' + iv_distance_km = 40 + iv_pitch_cents = 60 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '149.00' iv_travel = '24.00' + iv_extra = '27.00' iv_total = '200.00' ). + ENDMETHOD. + + METHOD unknown_type. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'Z' + iv_distance_km = 5 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge + iv_reason = 'UNKNOWN' iv_base = '0.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD negative_distance. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = -1 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge + iv_reason = 'DISTANCE' iv_base = '0.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD negative_pitch. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 5 + iv_pitch_cents = -1 ). + assert_charge( is_charge = ls_charge + iv_reason = 'PITCH' iv_base = '0.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD unknown_wins. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'Z' + iv_distance_km = -5 + iv_pitch_cents = -5 ). + assert_charge( is_charge = ls_charge + iv_reason = 'UNKNOWN' iv_base = '0.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD exact_limits. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 10 + iv_pitch_cents = 15 ). + assert_charge( is_charge = ls_charge + iv_reason = 'OK' iv_base = '89.00' iv_travel = '0.00' + iv_extra = '0.00' iv_total = '89.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/mutation.json b/tasks_gen/train/G1037/mutation.json new file mode 100644 index 0000000..2528f87 --- /dev/null +++ b/tasks_gen/train/G1037/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G1037", + "mutants": [ + { + "object": "{{P}}PIANO_PRICER", + "mutant": "line 63: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "CONCERT_GRAND", + "DIGITAL_PIANO", + "GRAND_PITCH_RAISE", + "STUDIO_PIANO" + ] + }, + { + "object": "{{P}}PIANO_PRICER", + "mutant": "line 74: <= -> > (rel)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "CONCERT_GRAND", + "DIGITAL_PIANO", + "GRAND_PITCH_RAISE", + "STUDIO_PIANO", + "TRAVEL_BEYOND_FREE", + "UPRIGHT_BASIC" + ] + }, + { + "object": "{{P}}PIANO_PRICER", + "mutant": "line 77: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "CONCERT_GRAND", + "GRAND_PITCH_RAISE", + "STUDIO_PIANO" + ] + }, + { + "object": "{{P}}PIANO_PRICER", + "mutant": "line 82: <= -> > (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "CONCERT_GRAND", + "GRAND_PITCH_RAISE", + "STUDIO_PIANO", + "TRAVEL_BEYOND_FREE", + "UPRIGHT_BASIC" + ] + }, + { + "object": "{{P}}PIANO_PRICER", + "mutant": "line 85: * -> / (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "CONCERT_GRAND", + "STUDIO_PIANO", + "TRAVEL_BEYOND_FREE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1037/reference/piano_pricer.clas.abap b/tasks_gen/train/G1037/reference/piano_pricer.clas.abap new file mode 100644 index 0000000..532ac7c --- /dev/null +++ b/tasks_gen/train/G1037/reference/piano_pricer.clas.abap @@ -0,0 +1,87 @@ +CLASS {{p}}piano_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_piano_type TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 8. + TYPES: + BEGIN OF ty_charge, + reason TYPE ty_reason, + base_fee TYPE ty_amount, + travel_fee TYPE ty_amount, + extra_fee TYPE ty_amount, + total_fee TYPE ty_amount, + END OF ty_charge. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + unknown TYPE ty_reason VALUE 'UNKNOWN', + distance TYPE ty_reason VALUE 'DISTANCE', + pitch TYPE ty_reason VALUE 'PITCH', + END OF c_reason. + METHODS calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. + + PRIVATE SECTION. + CONSTANTS c_standard_minutes TYPE i VALUE 45. + + METHODS rate_for + IMPORTING iv_piano_type TYPE ty_piano_type + RETURNING VALUE(rs_rate) TYPE {{p}}tune_rates=>ty_rate. + METHODS calc_extra_fee + IMPORTING iv_pitch_cents TYPE i + is_rate TYPE {{p}}tune_rates=>ty_rate + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS calc_travel_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}piano_pricer IMPLEMENTATION. + METHOD calculate. + DATA(ls_rate) = rate_for( iv_piano_type ). + IF ls_rate IS INITIAL. + rs_charge = VALUE #( reason = c_reason-unknown ). + RETURN. + ENDIF. + IF iv_distance_km < 0. + rs_charge = VALUE #( reason = c_reason-distance ). + RETURN. + ENDIF. + IF iv_pitch_cents < 0. + rs_charge = VALUE #( reason = c_reason-pitch ). + RETURN. + ENDIF. + rs_charge = VALUE #( reason = c_reason-ok + base_fee = ls_rate-base_fee + travel_fee = calc_travel_fee( iv_distance_km ) + extra_fee = calc_extra_fee( iv_pitch_cents = iv_pitch_cents + is_rate = ls_rate ) ). + rs_charge-total_fee = rs_charge-base_fee + rs_charge-travel_fee + rs_charge-extra_fee. + ENDMETHOD. + + METHOD rate_for. + DATA(lo_rates) = NEW {{p}}tune_rates( ). + DATA(lt_rate) = lo_rates->get_rates( ). + READ TABLE lt_rate WITH KEY piano_type = iv_piano_type INTO rs_rate. + ENDMETHOD. + + METHOD calc_extra_fee. + DATA(lv_extra_minutes) = c_standard_minutes + iv_pitch_cents - is_rate-incl_minutes. + IF lv_extra_minutes <= 0. + RETURN. + ENDIF. + rv_fee = lv_extra_minutes * is_rate-minute_fee. + ENDMETHOD. + + METHOD calc_travel_fee. + DATA(lv_chargeable_km) = iv_distance_km - {{p}}tune_rates=>c_free_km. + IF lv_chargeable_km <= 0. + RETURN. + ENDIF. + rv_fee = lv_chargeable_km * {{p}}tune_rates=>c_km_fee. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/reference/piano_pricer.testclasses.abap b/tasks_gen/train/G1037/reference/piano_pricer.testclasses.abap new file mode 100644 index 0000000..308e50a --- /dev/null +++ b/tasks_gen/train/G1037/reference/piano_pricer.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_piano_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}piano_pricer. + + METHODS setup. + METHODS assert_charge + IMPORTING is_charge TYPE {{p}}piano_pricer=>ty_charge + iv_reason TYPE {{p}}piano_pricer=>ty_reason + iv_base TYPE {{p}}piano_pricer=>ty_amount + iv_travel TYPE {{p}}piano_pricer=>ty_amount + iv_extra TYPE {{p}}piano_pricer=>ty_amount + iv_total TYPE {{p}}piano_pricer=>ty_amount. + + METHODS upright_visit FOR TESTING. + METHODS pitch_raise FOR TESTING. + METHODS far_visit FOR TESTING. + METHODS unknown_piano FOR TESTING. + METHODS invalid_distance FOR TESTING. + METHODS invalid_pitch FOR TESTING. +ENDCLASS. + + +CLASS ltc_piano_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}piano_pricer( ). + ENDMETHOD. + + METHOD assert_charge. + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_charge-reason ). + cl_abap_unit_assert=>assert_equals( exp = iv_base act = is_charge-base_fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_travel act = is_charge-travel_fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_extra act = is_charge-extra_fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_total act = is_charge-total_fee ). + ENDMETHOD. + + METHOD upright_visit. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 3 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge iv_reason = 'OK' iv_base = '89.00' + iv_travel = '0.00' iv_extra = '0.00' iv_total = '89.00' ). + ENDMETHOD. + + METHOD pitch_raise. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'G' + iv_distance_km = 0 + iv_pitch_cents = 40 ). + assert_charge( is_charge = ls_charge iv_reason = 'OK' iv_base = '129.00' + iv_travel = '0.00' iv_extra = '15.00' iv_total = '144.00' ). + ENDMETHOD. + + METHOD far_visit. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 25 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge iv_reason = 'OK' iv_base = '89.00' + iv_travel = '12.00' iv_extra = '0.00' iv_total = '101.00' ). + ENDMETHOD. + + METHOD unknown_piano. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'Z' + iv_distance_km = 0 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge iv_reason = 'UNKNOWN' iv_base = '0.00' + iv_travel = '0.00' iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD invalid_distance. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = -1 + iv_pitch_cents = 0 ). + assert_charge( is_charge = ls_charge iv_reason = 'DISTANCE' iv_base = '0.00' + iv_travel = '0.00' iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. + + METHOD invalid_pitch. + DATA(ls_charge) = mo_cut->calculate( iv_piano_type = 'U' + iv_distance_km = 0 + iv_pitch_cents = -1 ). + assert_charge( is_charge = ls_charge iv_reason = 'PITCH' iv_base = '0.00' + iv_travel = '0.00' iv_extra = '0.00' iv_total = '0.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/seed/tune_rates.clas.abap b/tasks_gen/train/G1037/seed/tune_rates.clas.abap new file mode 100644 index 0000000..8f15d47 --- /dev/null +++ b/tasks_gen/train/G1037/seed/tune_rates.clas.abap @@ -0,0 +1,30 @@ +CLASS {{p}}tune_rates DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_rate, + piano_type TYPE c LENGTH 1, + base_fee TYPE ty_amount, + incl_minutes TYPE i, + minute_fee TYPE ty_amount, + END OF ty_rate. + TYPES tt_rate TYPE STANDARD TABLE OF ty_rate WITH EMPTY KEY. + CONSTANTS: + c_free_km TYPE i VALUE 10, + c_km_fee TYPE ty_amount VALUE '0.80'. + METHODS get_rates + RETURNING VALUE(rt_rate) TYPE tt_rate. +ENDCLASS. + + +CLASS {{p}}tune_rates IMPLEMENTATION. + METHOD get_rates. + " U = upright, G = grand, D = digital, C = concert grand, S = studio + rt_rate = VALUE #( + ( piano_type = 'U' base_fee = '89.00' incl_minutes = 60 minute_fee = '1.20' ) + ( piano_type = 'G' base_fee = '129.00' incl_minutes = 75 minute_fee = '1.50' ) + ( piano_type = 'D' base_fee = '69.00' incl_minutes = 45 minute_fee = '1.00' ) + ( piano_type = 'C' base_fee = '149.00' incl_minutes = 90 minute_fee = '1.80' ) + ( piano_type = 'S' base_fee = '99.00' incl_minutes = 60 minute_fee = '1.30' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1037/spec.md b/tasks_gen/train/G1037/spec.md new file mode 100644 index 0000000..092f435 --- /dev/null +++ b/tasks_gen/train/G1037/spec.md @@ -0,0 +1,68 @@ +# 1. Goal +Calculate the charge of a piano tuning visit. A booking program calls the +calculation before it confirms the appointment. The program needs the fee parts +and the total fee of the visit. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}TUNE_RATES is in package $TMP. It is active. It holds the + price list of the tuning service and the travel constants. Read this class. + Its interface is not written in this document. +- {{P}}TUNE_RATES is out of scope. Do not change it. + +# 4. Contract +- Create the class {{P}}PIANO_PRICER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Define these public types in the class: + - ty_piano_type: character, length 1. + - ty_amount: packed number, length 8, 2 decimals. + - ty_reason: character, length 8. + - ty_charge: structure with the components reason (TYPE ty_reason), base_fee, + travel_fee, extra_fee and total_fee (all TYPE ty_amount). +- Define the public constant structure c_reason with the components ok = 'OK', + unknown = 'UNKNOWN', distance = 'DISTANCE' and pitch = 'PITCH'. All + components have the type ty_reason. +- The class has one public method: + calculate + IMPORTING iv_piano_type TYPE ty_piano_type + iv_distance_km TYPE i + iv_pitch_cents TYPE i + RETURNING VALUE(rs_charge) TYPE ty_charge. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. Read the price list of {{P}}TUNE_RATES. If iv_piano_type is not a piano type + of the price list, return a charge with the reason UNKNOWN. All fee + components of this charge are 0. +2. If iv_distance_km is smaller than 0, return a charge with the reason + DISTANCE. All fee components of this charge are 0. +3. If iv_pitch_cents is smaller than 0, return a charge with the reason PITCH. + All fee components of this charge are 0. +4. The base fee is the base fee of the piano type in the price list. +5. A tuning takes 45 minutes. Each cent of pitch deviation adds one minute of + work. +6. The piano type has a number of included minutes and a fee for each minute. + The extra fee is the number of work minutes above the included minutes + multiplied by the minute fee. If the work minutes are not above the included + minutes, the extra fee is 0. +7. The travel is free up to the free distance of {{P}}TUNE_RATES. Each + kilometre above the free distance costs the kilometre fee of + {{P}}TUNE_RATES. The travel fee is this cost. If the distance is not above + the free distance, the travel fee is 0. +8. The total fee is the base fee plus the travel fee plus the extra fee. +9. Else return a charge with the reason OK and the fee components of rules 4, + 6 and 7. + +# 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}}TUNE_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/G1037/task.json b/tasks_gen/train/G1037/task.json new file mode 100644 index 0000000..648482e --- /dev/null +++ b/tasks_gen/train/G1037/task.json @@ -0,0 +1,50 @@ +{ + "id": "G1037", + "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}}TUNE_RATES", + "file": "seed/tune_rates.clas.abap", + "description": "Price list of the tuning service and the travel constants. The spec does not give its signature." + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PIANO_PRICER" + } + ], + "out_of_scope": [ + "{{P}}TUNE_RATES" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T42_HIDDEN", + "file": "hidden/t42_hidden.clas.abap", + "description": "T42 hidden tests for the piano tuning charge" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PIANO_PRICER", + "file": "reference/piano_pricer.clas.abap", + "description": "Piano tuning charge calculator", + "testclasses_file": "reference/piano_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1038/faulty/m0_mkt_stall_fee.func.abap b/tasks_gen/train/G1038/faulty/m0_mkt_stall_fee.func.abap new file mode 100644 index 0000000..3c2a899 --- /dev/null +++ b/tasks_gen/train/G1038/faulty/m0_mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days >= 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power = abap_true. + lv_daily_fee = lv_daily_fee + c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee * c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/faulty/m1_mkt_stall_fee.func.abap b/tasks_gen/train/G1038/faulty/m1_mkt_stall_fee.func.abap new file mode 100644 index 0000000..3019d82 --- /dev/null +++ b/tasks_gen/train/G1038/faulty/m1_mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power <> abap_true. + lv_daily_fee = lv_daily_fee + c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee * c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/faulty/m2_mkt_stall_fee.func.abap b/tasks_gen/train/G1038/faulty/m2_mkt_stall_fee.func.abap new file mode 100644 index 0000000..e434125 --- /dev/null +++ b/tasks_gen/train/G1038/faulty/m2_mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power = abap_false. + lv_daily_fee = lv_daily_fee + c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee * c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/faulty/m3_mkt_stall_fee.func.abap b/tasks_gen/train/G1038/faulty/m3_mkt_stall_fee.func.abap new file mode 100644 index 0000000..5125cdb --- /dev/null +++ b/tasks_gen/train/G1038/faulty/m3_mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power = abap_true. + lv_daily_fee = lv_daily_fee - c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee * c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/faulty/m4_mkt_stall_fee.func.abap b/tasks_gen/train/G1038/faulty/m4_mkt_stall_fee.func.abap new file mode 100644 index 0000000..f54b4f1 --- /dev/null +++ b/tasks_gen/train/G1038/faulty/m4_mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power = abap_true. + lv_daily_fee = lv_daily_fee + c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee / c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/generation.json b/tasks_gen/train/G1038/generation.json new file mode 100644 index 0000000..f65b485 --- /dev/null +++ b/tasks_gen/train/G1038/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G1038", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "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/G1038/hidden/t_mkt_hidden.clas.abap b/tasks_gen/train/G1038/hidden/t_mkt_hidden.clas.abap new file mode 100644 index 0000000..6d155a9 --- /dev/null +++ b/tasks_gen/train/G1038/hidden/t_mkt_hidden.clas.abap @@ -0,0 +1,99 @@ +CLASS {{p}}t_mkt_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS small_one_day FOR TESTING. + METHODS medium_two_days_power FOR TESTING. + METHODS large_one_day FOR TESTING. + METHODS no_reduction_four_days FOR TESTING. + METHODS reduction_five_days FOR TESTING. + METHODS reduction_rounding FOR TESTING. + METHODS reduction_large_six_days FOR TESTING. + METHODS invalid_days_zero FOR TESTING. + METHODS invalid_days_negative FOR TESTING. + METHODS invalid_size FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_mkt_hidden IMPLEMENTATION. + METHOD call. + ev_fee = 0. + CALL FUNCTION '{{P}}MKT_STALL_FEE' + EXPORTING + iv_stall_size = iv_size + iv_days = iv_days + iv_power = iv_power + IMPORTING + ev_fee = ev_fee + EXCEPTIONS + invalid_days = 1 + invalid_size = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD small_one_day. + call( EXPORTING iv_size = 'S' iv_days = 1 iv_power = abap_false + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD medium_two_days_power. + call( EXPORTING iv_size = 'M' iv_days = 2 iv_power = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD large_one_day. + call( EXPORTING iv_size = 'L' iv_days = 1 iv_power = abap_false + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD no_reduction_four_days. + call( EXPORTING iv_size = 'M' iv_days = 4 iv_power = abap_false + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '80.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD reduction_five_days. + call( EXPORTING iv_size = 'S' iv_days = 5 iv_power = abap_false + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '51.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD reduction_rounding. + call( EXPORTING iv_size = 'S' iv_days = 5 iv_power = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '61.62' ) act = lv_fee ). + ENDMETHOD. + + METHOD reduction_large_six_days. + call( EXPORTING iv_size = 'L' iv_days = 6 iv_power = abap_true + IMPORTING ev_fee = DATA(lv_fee) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '191.25' ) act = lv_fee ). + ENDMETHOD. + + METHOD invalid_days_zero. + call( EXPORTING iv_size = 'S' iv_days = 0 iv_power = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD invalid_days_negative. + call( EXPORTING iv_size = 'M' iv_days = -3 iv_power = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. + + METHOD invalid_size. + call( EXPORTING iv_size = 'X' iv_days = 3 iv_power = abap_false + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1038/mutation.json b/tasks_gen/train/G1038/mutation.json new file mode 100644 index 0000000..0e42d1f --- /dev/null +++ b/tasks_gen/train/G1038/mutation.json @@ -0,0 +1,79 @@ +{ + "task": "G1038", + "mutants": [ + { + "object": "{{P}}MKT_STALL_FEE", + "mutant": "line 25: < -> >= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "INVALID_DAYS_NEGATIVE", + "INVALID_DAYS_ZERO", + "INVALID_SIZE", + "LARGE_ONE_DAY", + "MEDIUM_TWO_DAYS_POWER", + "NO_REDUCTION_FOUR_DAYS", + "REDUCTION_FIVE_DAYS", + "REDUCTION_LARGE_SIX_DAYS", + "REDUCTION_ROUNDING", + "SMALL_ONE_DAY" + ] + }, + { + "object": "{{P}}MKT_STALL_FEE", + "mutant": "line 40: = -> <> (eq)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "LARGE_ONE_DAY", + "MEDIUM_TWO_DAYS_POWER", + "NO_REDUCTION_FOUR_DAYS", + "REDUCTION_FIVE_DAYS", + "REDUCTION_LARGE_SIX_DAYS", + "REDUCTION_ROUNDING", + "SMALL_ONE_DAY" + ] + }, + { + "object": "{{P}}MKT_STALL_FEE", + "mutant": "line 40: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "LARGE_ONE_DAY", + "MEDIUM_TWO_DAYS_POWER", + "NO_REDUCTION_FOUR_DAYS", + "REDUCTION_FIVE_DAYS", + "REDUCTION_LARGE_SIX_DAYS", + "REDUCTION_ROUNDING", + "SMALL_ONE_DAY" + ] + }, + { + "object": "{{P}}MKT_STALL_FEE", + "mutant": "line 41: + -> - (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "MEDIUM_TWO_DAYS_POWER", + "REDUCTION_LARGE_SIX_DAYS", + "REDUCTION_ROUNDING" + ] + }, + { + "object": "{{P}}MKT_STALL_FEE", + "mutant": "line 47: * -> / (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "REDUCTION_FIVE_DAYS", + "REDUCTION_LARGE_SIX_DAYS", + "REDUCTION_ROUNDING" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1038/reference/mkt_fee_test.clas.abap b/tasks_gen/train/G1038/reference/mkt_fee_test.clas.abap new file mode 100644 index 0000000..03ec714 --- /dev/null +++ b/tasks_gen/train/G1038/reference/mkt_fee_test.clas.abap @@ -0,0 +1,89 @@ +CLASS {{p}}mkt_fee_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS daily_fee_by_size FOR TESTING. + METHODS power_surcharge FOR TESTING. + METHODS reduction_from_five_days FOR TESTING. + METHODS reduction_is_rounded FOR TESTING. + METHODS invalid_days FOR TESTING. + METHODS invalid_size FOR TESTING. +ENDCLASS. + + +CLASS {{p}}mkt_fee_test IMPLEMENTATION. + METHOD fee_for. + CALL FUNCTION '{{P}}MKT_STALL_FEE' + EXPORTING + iv_stall_size = iv_size + iv_days = iv_days + iv_power = iv_power + IMPORTING + ev_fee = rv_fee + EXCEPTIONS + invalid_days = 1 + invalid_size = 2 + OTHERS = 3. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD daily_fee_by_size. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.00' ) + act = fee_for( iv_size = 'S' iv_days = 1 iv_power = abap_false ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = fee_for( iv_size = 'M' iv_days = 1 iv_power = abap_false ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.00' ) + act = fee_for( iv_size = 'L' iv_days = 1 iv_power = abap_false ) ). + ENDMETHOD. + + METHOD power_surcharge. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '45.00' ) + act = fee_for( iv_size = 'M' iv_days = 2 iv_power = abap_true ) ). + ENDMETHOD. + + METHOD reduction_from_five_days. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '80.00' ) + act = fee_for( iv_size = 'M' iv_days = 4 iv_power = abap_false ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '51.00' ) + act = fee_for( iv_size = 'S' iv_days = 5 iv_power = abap_false ) ). + ENDMETHOD. + + METHOD reduction_is_rounded. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '61.62' ) + act = fee_for( iv_size = 'S' iv_days = 5 iv_power = abap_true ) ). + ENDMETHOD. + + METHOD invalid_days. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}MKT_STALL_FEE' + EXPORTING + iv_stall_size = 'S' + iv_days = 0 + iv_power = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_days = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. + + METHOD invalid_size. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}MKT_STALL_FEE' + EXPORTING + iv_stall_size = 'Z' + iv_days = 2 + iv_power = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_size = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1038/reference/mkt_stall_fee.func.abap b/tasks_gen/train/G1038/reference/mkt_stall_fee.func.abap new file mode 100644 index 0000000..dbbfb54 --- /dev/null +++ b/tasks_gen/train/G1038/reference/mkt_stall_fee.func.abap @@ -0,0 +1,53 @@ +FUNCTION {{p}}mkt_stall_fee + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + CONSTANTS c_small TYPE decfloat34 VALUE '12.00'. + CONSTANTS c_medium TYPE decfloat34 VALUE '20.00'. + CONSTANTS c_large TYPE decfloat34 VALUE '35.00'. + CONSTANTS c_power TYPE decfloat34 VALUE '2.50'. + CONSTANTS c_rebate TYPE decfloat34 VALUE '15'. + CONSTANTS c_hundred TYPE decfloat34 VALUE '100'. + + DATA lv_daily_fee TYPE decfloat34. + DATA lv_total_fee TYPE decfloat34. + DATA lv_reduction TYPE decfloat34. + + ev_fee = 0. + + IF iv_days < 1. + RAISE invalid_days. + ENDIF. + + CASE iv_stall_size. + WHEN 'S'. + lv_daily_fee = c_small. + WHEN 'M'. + lv_daily_fee = c_medium. + WHEN 'L'. + lv_daily_fee = c_large. + WHEN OTHERS. + RAISE invalid_size. + ENDCASE. + + IF iv_power = abap_true. + lv_daily_fee = lv_daily_fee + c_power. + ENDIF. + + lv_total_fee = lv_daily_fee * iv_days. + + IF iv_days >= 5. + lv_reduction = round( val = lv_total_fee * c_rebate / c_hundred dec = 2 ). + ev_fee = lv_total_fee - lv_reduction. + ELSE. + ev_fee = lv_total_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/seed/mkt_sample.func.abap b/tasks_gen/train/G1038/seed/mkt_sample.func.abap new file mode 100644 index 0000000..eac8ef7 --- /dev/null +++ b/tasks_gen/train/G1038/seed/mkt_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}mkt_sample + IMPORTING + iv_stall_size TYPE string + iv_days TYPE i + iv_power TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_days + invalid_size. + + ev_fee = 0. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1038/spec.md b/tasks_gen/train/G1038/spec.md new file mode 100644 index 0000000..a3dabfc --- /dev/null +++ b/tasks_gen/train/G1038/spec.md @@ -0,0 +1,57 @@ +# 1. Goal +A market operator hires out stalls to traders at its weekly market. For each +booking the operator charges a stall fee. The booking program calls one +function module for each booking. The function module calculates the fee from +the stall size, the number of market days, and whether the trader needs an +electricity supply. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_MKT exists in package $TMP. +- Function module {{P}}MKT_SAMPLE exists in this group. It is the sample + module. Its interface is the interface that the booking program uses. +- The booking program calls {{P}}MKT_STALL_FEE for each booking and reads the + exported fee. + +# 4. Contract +Create the function module {{P}}MKT_STALL_FEE in function group {{P}}FG_MKT. +Use exactly the interface of {{P}}MKT_SAMPLE. + +| Parameter | Kind | Type | Meaning | +| --- | --- | --- | --- | +| iv_stall_size | importing | string | Stall size: 'S', 'M' or 'L' | +| iv_days | importing | i | Number of market days | +| iv_power | importing | abap_bool | Electricity supply is wanted | +| ev_fee | exporting | decfloat34 | Calculated stall fee | +| invalid_days | exception | - | iv_days is less than 1 | +| invalid_size | exception | - | iv_stall_size is not 'S', 'M' or 'L' | + +# 5. Business rules +1. If iv_days is less than 1, raise the exception INVALID_DAYS and do not + calculate a fee. Check this rule before rule 2. +2. The daily fee depends on iv_stall_size: + - 'S': 12.00 + - 'M': 20.00 + - 'L': 35.00 + Any other value of iv_stall_size raises the exception INVALID_SIZE and no + fee is calculated. +3. If iv_power is abap_true, add 2.50 to the daily fee. +4. The total fee is the daily fee multiplied by iv_days. +5. If iv_days is 5 or more, the total fee is reduced by 15 %. Calculate the + reduction from the total fee, round the reduction half up to 2 decimals, + and subtract the rounded reduction from the total fee. +6. If iv_days is less than 5, the fee is the total fee without a reduction. + +# 6. Constraints +- Release target: 7.40 SP05. Use only syntax that is available in this release. + Do not use the new ABAP SQL syntax with @ host variables or comma separated + field lists. +- Coding standards: Clean ABAP. Keep every method below 40 statements. +- Out of scope: do not change the function module {{P}}MKT_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/G1038/task.json b/tasks_gen/train/G1038/task.json new file mode 100644 index 0000000..3a2df62 --- /dev/null +++ b/tasks_gen/train/G1038/task.json @@ -0,0 +1,80 @@ +{ + "id": "G1038", + "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_MKT", + "description": "Market stall fees" + }, + { + "type": "FUNC", + "name": "{{P}}MKT_SAMPLE", + "functionGroup": "{{P}}FG_MKT", + "file": "seed/mkt_sample.func.abap", + "description": "Sample stall fee module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}MKT_STALL_FEE", + "functionGroup": "{{P}}FG_MKT", + "params": [ + { + "name": "iv_stall_size", + "type": "string" + }, + { + "name": "iv_days", + "type": "i" + }, + { + "name": "iv_power", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}MKT_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_MKT_HIDDEN", + "file": "hidden/t_mkt_hidden.clas.abap", + "description": "Hidden tests for the stall fee" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}MKT_STALL_FEE", + "functionGroup": "{{P}}FG_MKT", + "file": "reference/mkt_stall_fee.func.abap", + "description": "Stall fee module" + }, + { + "type": "CLAS", + "name": "{{P}}MKT_FEE_TEST", + "file": "reference/mkt_fee_test.clas.abap", + "description": "Own tests for the stall fee" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1039/generation.json b/tasks_gen/train/G1039/generation.json new file mode 100644 index 0000000..07d077c --- /dev/null +++ b/tasks_gen/train/G1039/generation.json @@ -0,0 +1,14 @@ +{ + "id": "G1039", + "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/G1039/reference/tour_summary.clas.abap b/tasks_gen/train/G1039/reference/tour_summary.clas.abap new file mode 100644 index 0000000..1d8effd --- /dev/null +++ b/tasks_gen/train/G1039/reference/tour_summary.clas.abap @@ -0,0 +1,76 @@ +CLASS {{p}}tour_summary DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_tour_summary. + + PRIVATE SECTION. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_fee_classic TYPE ty_fee VALUE '20.00', + c_fee_premium TYPE ty_fee VALUE '30.00', + c_child_share TYPE ty_fee VALUE '0.50', + c_group_discount TYPE ty_fee VALUE '0.90', + c_group_size TYPE i VALUE 10. + + METHODS validate + IMPORTING it_bookings TYPE {{p}}if_tour_summary=>tt_booking + RETURNING VALUE(rv_status) TYPE {{p}}if_tour_summary=>ty_status. + METHODS booking_fee + IMPORTING is_booking TYPE {{p}}if_tour_summary=>ty_booking + RETURNING VALUE(rv_fee) TYPE ty_fee. + METHODS adult_fee + IMPORTING iv_tour_type TYPE {{p}}if_tour_summary=>ty_tour_type + RETURNING VALUE(rv_fee) TYPE ty_fee. +ENDCLASS. + + +CLASS {{p}}tour_summary IMPLEMENTATION. + METHOD {{p}}if_tour_summary~summarize. + DATA(lv_status) = validate( it_bookings ). + IF lv_status <> {{p}}if_tour_summary=>c_status-ok. + rs_result = VALUE #( status = lv_status ). + RETURN. + ENDIF. + + LOOP AT it_bookings INTO DATA(ls_booking). + rs_result-total_fee = rs_result-total_fee + booking_fee( ls_booking ). + ENDLOOP. + + rs_result-booking_count = lines( it_bookings ). + IF rs_result-booking_count > 0. + rs_result-average_fee = rs_result-total_fee / rs_result-booking_count. + ENDIF. + rs_result-status = {{p}}if_tour_summary=>c_status-ok. + ENDMETHOD. + + METHOD validate. + rv_status = {{p}}if_tour_summary=>c_status-ok. + LOOP AT it_bookings INTO DATA(ls_booking). + IF ls_booking-adults < 0 OR ls_booking-children < 0. + rv_status = {{p}}if_tour_summary=>c_status-invalid_number. + RETURN. + ENDIF. + IF ls_booking-tour_type <> 'C' AND ls_booking-tour_type <> 'P'. + rv_status = {{p}}if_tour_summary=>c_status-unknown_type. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD booking_fee. + DATA(lv_adult_fee) = adult_fee( is_booking-tour_type ). + rv_fee = is_booking-adults * lv_adult_fee + + is_booking-children * lv_adult_fee * c_child_share. + IF is_booking-adults + is_booking-children >= c_group_size. + rv_fee = rv_fee * c_group_discount. + ENDIF. + ENDMETHOD. + + METHOD adult_fee. + IF iv_tour_type = 'P'. + rv_fee = c_fee_premium. + ELSE. + rv_fee = c_fee_classic. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1039/reference/tour_summary.testclasses.abap b/tasks_gen/train/G1039/reference/tour_summary.testclasses.abap new file mode 100644 index 0000000..f68ad8d --- /dev/null +++ b/tasks_gen/train/G1039/reference/tour_summary.testclasses.abap @@ -0,0 +1,102 @@ +CLASS ltc_tour_summary DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_fee TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + c_fee_0 TYPE ty_fee VALUE '0.00', + c_fee_25 TYPE ty_fee VALUE '25.00', + c_fee_30 TYPE ty_fee VALUE '30.00', + c_fee_40 TYPE ty_fee VALUE '40.00', + c_fee_50 TYPE ty_fee VALUE '50.00', + c_fee_180 TYPE ty_fee VALUE '180.00'. + + DATA mo_cut TYPE REF TO {{p}}tour_summary. + + METHODS setup. + METHODS classic_adults FOR TESTING. + METHODS premium_adult FOR TESTING. + METHODS child_pays_half FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS average_of_two FOR TESTING. + METHODS negative_number FOR TESTING. + METHODS unknown_type FOR TESTING. + METHODS empty_day FOR TESTING. +ENDCLASS. + + +CLASS ltc_tour_summary IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tour_summary( ). + ENDMETHOD. + + METHOD classic_adults. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'C' adults = 2 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_40 act = ls_result-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_result-booking_count ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_40 act = ls_result-average_fee ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tour_summary=>c_status-ok + act = ls_result-status ). + ENDMETHOD. + + METHOD premium_adult. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'P' adults = 1 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_30 act = ls_result-total_fee ). + ENDMETHOD. + + METHOD child_pays_half. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'C' adults = 1 children = 2 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_40 act = ls_result-total_fee ). + ENDMETHOD. + + METHOD group_discount. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'C' adults = 10 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_180 act = ls_result-total_fee ). + ENDMETHOD. + + METHOD average_of_two. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'C' adults = 1 children = 0 ) + ( tour_type = 'P' adults = 1 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = c_fee_50 act = ls_result-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_result-booking_count ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_25 act = ls_result-average_fee ). + ENDMETHOD. + + METHOD negative_number. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'C' adults = -1 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tour_summary=>c_status-invalid_number + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_0 act = ls_result-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-booking_count ). + ENDMETHOD. + + METHOD unknown_type. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( + VALUE #( ( tour_type = 'X' adults = 1 children = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tour_summary=>c_status-unknown_type + act = ls_result-status ). + ENDMETHOD. + + METHOD empty_day. + DATA lt_empty TYPE {{p}}if_tour_summary=>tt_booking. + DATA(ls_result) = mo_cut->{{p}}if_tour_summary~summarize( lt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_tour_summary=>c_status-ok + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_0 act = ls_result-total_fee ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-booking_count ). + cl_abap_unit_assert=>assert_equals( exp = c_fee_0 act = ls_result-average_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1039/seed/if_tour_summary.intf.abap b/tasks_gen/train/G1039/seed/if_tour_summary.intf.abap new file mode 100644 index 0000000..c2b97b5 --- /dev/null +++ b/tasks_gen/train/G1039/seed/if_tour_summary.intf.abap @@ -0,0 +1,32 @@ +INTERFACE {{p}}if_tour_summary PUBLIC. + TYPES ty_tour_type TYPE c LENGTH 1. + TYPES ty_status TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_booking, + tour_type TYPE ty_tour_type, + adults TYPE i, + children TYPE i, + END OF ty_booking. + + TYPES tt_booking TYPE STANDARD TABLE OF ty_booking WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + total_fee TYPE p LENGTH 8 DECIMALS 2, + booking_count TYPE i, + average_fee TYPE p LENGTH 8 DECIMALS 2, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'O', + invalid_number TYPE ty_status VALUE 'N', + unknown_type TYPE ty_status VALUE 'T', + END OF c_status. + + METHODS summarize + IMPORTING it_bookings TYPE tt_booking + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1039/spec.md b/tasks_gen/train/G1039/spec.md new file mode 100644 index 0000000..d148054 --- /dev/null +++ b/tasks_gen/train/G1039/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A tour operator wants a summary of the booking fees of one day. The booking +program calls one class for each day of the month and shows the summary. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_TOUR_SUMMARY exists in package $TMP. It is active. +- The interface has the types TY_TOUR_TYPE, TY_STATUS, TY_BOOKING, TT_BOOKING + and TY_RESULT, the constant structure C_STATUS, and one method: + SUMMARIZE IMPORTING it_bookings TYPE tt_booking + RETURNING VALUE(rs_result) TYPE ty_result. +- The booking program passes the bookings of one day to the method. +- A day can have no bookings. + +# 4. Contract +- Create the class {{P}}TOUR_SUMMARY in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_TOUR_SUMMARY. +- Do not add other public methods. + +# 5. Business rules +1. A tour type is 'C' (classic) or 'P' (premium). +2. If one booking has a negative number of adults or a negative number of + children, the summary is invalid. Return status 'N' and 0 in the fields + total_fee, booking_count and average_fee. +3. If one booking has a tour type that is not 'C' and not 'P', the summary is + invalid. Return status 'T' and 0 in the fields total_fee, booking_count and + average_fee. +4. Check rule 2 before rule 3. +5. The fee of one booking: an adult pays 20.00 for a classic tour and 30.00 for + a premium tour. A child pays half of the adult fee of the tour. +6. If the number of participants of one booking (adults plus children) is 10 or + more, reduce the fee of this booking by 10 percent. +7. Round the fee of each booking to 2 decimals. Round half up. +8. total_fee is the sum of the fees of all bookings. +9. booking_count is the number of bookings. +10. average_fee is total_fee divided by booking_count. Round half up to + 2 decimals. +11. If the summary is valid, return status 'O'. + +# 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_TOUR_SUMMARY. + +# 7. Acceptance +- The class is active and has no syntax error. +- The class implements {{P}}IF_TOUR_SUMMARY. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1039/task.json b/tasks_gen/train/G1039/task.json new file mode 100644 index 0000000..925c34f --- /dev/null +++ b/tasks_gen/train/G1039/task.json @@ -0,0 +1,60 @@ +{ + "id": "G1039", + "category": "H", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "stop", + "expected_gap": { + "description": "The spec does not define the result of SUMMARIZE when the booking list is empty, so the average fee is a division by zero and the status for that case is undefined.", + "keywords": [ + [ + "empty", + "booking" + ], + [ + "division", + "zero" + ], + [ + "status", + "empty" + ] + ] + }, + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_TOUR_SUMMARY", + "file": "seed/if_tour_summary.intf.abap", + "description": "Tour summary contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TOUR_SUMMARY", + "implements": "{{P}}IF_TOUR_SUMMARY" + } + ], + "out_of_scope": [ + "{{P}}IF_TOUR_SUMMARY" + ], + "hidden_tests": [], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOUR_SUMMARY", + "file": "reference/tour_summary.clas.abap", + "description": "Tour summary", + "testclasses_file": "reference/tour_summary.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1040/faulty/m0_i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/faulty/m0_i_roll_grade.ddls.asddls new file mode 100644 index 0000000..322e846 --- /dev/null +++ b/tasks_gen/train/G1040/faulty/m0_i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric <> roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'S' end as fibre_class, + substring( roll.roll_id, 1, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 2 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects <= 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/faulty/m1_i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/faulty/m1_i_roll_grade.ddls.asddls new file mode 100644 index 0000000..b676955 --- /dev/null +++ b/tasks_gen/train/G1040/faulty/m1_i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric = roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'Z' end as fibre_class, + substring( roll.roll_id, 1, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 2 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects <= 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/faulty/m2_i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/faulty/m2_i_roll_grade.ddls.asddls new file mode 100644 index 0000000..86fe12c --- /dev/null +++ b/tasks_gen/train/G1040/faulty/m2_i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric = roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'S' end as fibre_class, + substring( roll.roll_id, 2, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 2 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects <= 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/faulty/m3_i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/faulty/m3_i_roll_grade.ddls.asddls new file mode 100644 index 0000000..16d4b2a --- /dev/null +++ b/tasks_gen/train/G1040/faulty/m3_i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric = roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'S' end as fibre_class, + substring( roll.roll_id, 1, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 3 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects <= 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/faulty/m4_i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/faulty/m4_i_roll_grade.ddls.asddls new file mode 100644 index 0000000..b47e6b8 --- /dev/null +++ b/tasks_gen/train/G1040/faulty/m4_i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric = roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'S' end as fibre_class, + substring( roll.roll_id, 1, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 2 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects > 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/generation.json b/tasks_gen/train/G1040/generation.json new file mode 100644 index 0000000..03b5bf3 --- /dev/null +++ b/tasks_gen/train/G1040/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1040", + "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/G1040/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1040/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..288fbfb --- /dev/null +++ b/tasks_gen/train/G1040/hidden/t16_hidden.clas.abap @@ -0,0 +1,97 @@ +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 grade_a FOR TESTING. + METHODS grade_b FOR TESTING. + METHODS grade_c FOR TESTING. + METHODS fibre_natural FOR TESTING. + METHODS fibre_synthetic FOR TESTING. + METHODS loom_prefix FOR TESTING. + METHODS area_rounded FOR TESTING. + METHODS area_whole FOR TESTING. + METHODS fabric_name FOR TESTING. + METHODS roll_without_fabric FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROLL_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_fabric TYPE STANDARD TABLE OF {{p}}fabric WITH EMPTY KEY. + DATA lt_roll TYPE STANDARD TABLE OF {{p}}roll WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_fabric = VALUE #( ( fabric = 'F1' fabric_name = 'COTTON POPLIN' fibre = 'C' ) + ( fabric = 'F2' fabric_name = 'WOOL TWILL' fibre = 'W' ) + ( fabric = 'F3' fabric_name = 'SILK SATIN' fibre = 'S' ) + ( fabric = 'F4' fabric_name = 'POLYESTER' fibre = 'P' ) ). + lt_roll = VALUE #( ( roll_id = 'L01-R-0001' fabric = 'F1' length_cm = 123 width_cm = 45 defects = 0 ) + ( roll_id = 'L02-R-0002' fabric = 'F2' length_cm = 200 width_cm = 150 defects = 3 ) + ( roll_id = 'L03-R-0003' fabric = 'F4' length_cm = 100 width_cm = 100 defects = 4 ) + ( roll_id = 'L04-R-0004' fabric = 'F9' length_cm = 100 width_cm = 100 defects = 1 ) ). + go_env->insert_test_data( lt_fabric ). + go_env->insert_test_data( lt_roll ). + ENDMETHOD. + + METHOD grade_a. + SELECT SINGLE grade FROM {{p}}i_roll_grade WHERE roll_id = 'L01-R-0001' INTO @DATA(lv_grade). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = lv_grade ). + ENDMETHOD. + + METHOD grade_b. + SELECT SINGLE grade FROM {{p}}i_roll_grade WHERE roll_id = 'L02-R-0002' INTO @DATA(lv_grade). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lv_grade ). + ENDMETHOD. + + METHOD grade_c. + SELECT SINGLE grade FROM {{p}}i_roll_grade WHERE roll_id = 'L03-R-0003' INTO @DATA(lv_grade). + cl_abap_unit_assert=>assert_equals( exp = 'C' act = lv_grade ). + ENDMETHOD. + + METHOD fibre_natural. + SELECT SINGLE fibre_class FROM {{p}}i_roll_grade WHERE roll_id = 'L02-R-0002' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'N' act = lv_class ). + ENDMETHOD. + + METHOD fibre_synthetic. + SELECT SINGLE fibre_class FROM {{p}}i_roll_grade WHERE roll_id = 'L03-R-0003' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lv_class ). + ENDMETHOD. + + METHOD loom_prefix. + SELECT SINGLE loom FROM {{p}}i_roll_grade WHERE roll_id = 'L02-R-0002' INTO @DATA(lv_loom). + cl_abap_unit_assert=>assert_equals( exp = 'L02' act = lv_loom ). + ENDMETHOD. + + METHOD area_rounded. + SELECT SINGLE area_sqm FROM {{p}}i_roll_grade WHERE roll_id = 'L01-R-0001' INTO @DATA(lv_area). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.55' ) + act = CONV decfloat34( lv_area ) ). + ENDMETHOD. + + METHOD area_whole. + SELECT SINGLE area_sqm FROM {{p}}i_roll_grade WHERE roll_id = 'L02-R-0002' INTO @DATA(lv_area). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.00' ) + act = CONV decfloat34( lv_area ) ). + ENDMETHOD. + + METHOD fabric_name. + SELECT SINGLE fabric_name FROM {{p}}i_roll_grade WHERE roll_id = 'L02-R-0002' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'WOOL TWILL' act = lv_name ). + ENDMETHOD. + + METHOD roll_without_fabric. + SELECT COUNT(*) FROM {{p}}i_roll_grade INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1040/mutation.json b/tasks_gen/train/G1040/mutation.json new file mode 100644 index 0000000..caa92da --- /dev/null +++ b/tasks_gen/train/G1040/mutation.json @@ -0,0 +1,57 @@ +{ + "task": "G1040", + "mutants": [ + { + "object": "{{P}}I_ROLL_GRADE", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "FABRIC_NAME", + "FIBRE_SYNTHETIC", + "ROLL_WITHOUT_FABRIC" + ] + }, + { + "object": "{{P}}I_ROLL_GRADE", + "mutant": "line 12: 'S' -> 'Z' (lit)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "FIBRE_SYNTHETIC" + ] + }, + { + "object": "{{P}}I_ROLL_GRADE", + "mutant": "line 13: 1 -> 2 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "LOOM_PREFIX" + ] + }, + { + "object": "{{P}}I_ROLL_GRADE", + "mutant": "line 16: 2 -> 3 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "AREA_ROUNDED" + ] + }, + { + "object": "{{P}}I_ROLL_GRADE", + "mutant": "line 18: <= -> > (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "GRADE_B", + "GRADE_C" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1040/reference/i_roll_grade.ddls.asddls b/tasks_gen/train/G1040/reference/i_roll_grade.ddls.asddls new file mode 100644 index 0000000..3f0146c --- /dev/null +++ b/tasks_gen/train/G1040/reference/i_roll_grade.ddls.asddls @@ -0,0 +1,20 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Fabric roll grade' +define view entity {{P}}I_ROLL_GRADE + as select from {{p}}roll as roll + inner join {{p}}fabric as fabric on fabric.fabric = roll.fabric +{ + key roll.roll_id as roll_id, + fabric.fabric_name as fabric_name, + case when fabric.fibre = 'C' or fabric.fibre = 'W' + or fabric.fibre = 'S' + then 'N' + else 'S' end as fibre_class, + substring( roll.roll_id, 1, 3 ) as loom, + round( cast( roll.length_cm as abap.dec(15,2) ) + * cast( roll.width_cm as abap.dec(15,2) ) + / 10000, 2 ) as area_sqm, + case when roll.defects = 0 then 'A' + when roll.defects <= 3 then 'B' + else 'C' end as grade +} diff --git a/tasks_gen/train/G1040/reference/t16_test.clas.abap b/tasks_gen/train/G1040/reference/t16_test.clas.abap new file mode 100644 index 0000000..b86c8c8 --- /dev/null +++ b/tasks_gen/train/G1040/reference/t16_test.clas.abap @@ -0,0 +1,46 @@ +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 grade_and_area FOR TESTING. + METHODS natural_fibre FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_ROLL_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_fabric TYPE STANDARD TABLE OF {{p}}fabric WITH EMPTY KEY. + DATA lt_roll TYPE STANDARD TABLE OF {{p}}roll WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_fabric = VALUE #( ( fabric = 'F1' fabric_name = 'COTTON POPLIN' fibre = 'C' ) ). + lt_roll = VALUE #( ( roll_id = 'L01-R-0001' fabric = 'F1' length_cm = 123 width_cm = 45 defects = 0 ) ). + go_env->insert_test_data( lt_fabric ). + go_env->insert_test_data( lt_roll ). + ENDMETHOD. + + METHOD grade_and_area. + SELECT SINGLE grade, area_sqm, loom FROM {{p}}i_roll_grade + WHERE roll_id = 'L01-R-0001' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 'A' act = ls_row-grade ). + cl_abap_unit_assert=>assert_equals( exp = 'L01' act = ls_row-loom ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.55' ) + act = CONV decfloat34( ls_row-area_sqm ) ). + ENDMETHOD. + + METHOD natural_fibre. + SELECT SINGLE fibre_class FROM {{p}}i_roll_grade + WHERE roll_id = 'L01-R-0001' INTO @DATA(lv_class). + cl_abap_unit_assert=>assert_equals( exp = 'N' act = lv_class ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1040/seed/fabric.tabl.asabap b/tasks_gen/train/G1040/seed/fabric.tabl.asabap new file mode 100644 index 0000000..cda07bc --- /dev/null +++ b/tasks_gen/train/G1040/seed/fabric.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Fabric master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}fabric { + key client : abap.clnt not null; + key fabric : abap.char(10) not null; + fabric_name : abap.char(20); + fibre : abap.char(1); +} diff --git a/tasks_gen/train/G1040/seed/roll.tabl.asabap b/tasks_gen/train/G1040/seed/roll.tabl.asabap new file mode 100644 index 0000000..587db56 --- /dev/null +++ b/tasks_gen/train/G1040/seed/roll.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Fabric roll' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}roll { + key client : abap.clnt not null; + key roll_id : abap.char(10) not null; + fabric : abap.char(10); + length_cm : abap.int4; + width_cm : abap.int4; + defects : abap.int4; +} diff --git a/tasks_gen/train/G1040/spec.md b/tasks_gen/train/G1040/spec.md new file mode 100644 index 0000000..81455e0 --- /dev/null +++ b/tasks_gen/train/G1040/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A textile mill inspects the fabric rolls of its looms. The quality team needs +one row for each roll with the fabric name, the fibre class, the loom, the +area and the grade. A report and an OData service will read the rows, so the +calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}FABRIC (fabric master), package $TMP. Key: FABRIC (CHAR 10). + Fields: FABRIC_NAME (CHAR 20), FIBRE (CHAR 1). + FIBRE is 'C' for cotton, 'W' for wool, 'S' for silk and 'P' for polyester. +- Table {{P}}ROLL (fabric roll), package $TMP. Key: ROLL_ID (CHAR 10). + Fields: FABRIC (CHAR 10), LENGTH_CM (INT4), WIDTH_CM (INT4), DEFECTS (INT4). + The first three characters of ROLL_ID identify the loom that wove the roll. + +# 4. Contract +- Create the CDS view entity {{P}}I_ROLL_GRADE in package $TMP. +- Elements, with these names: ROLL_ID (key), FABRIC_NAME, FIBRE_CLASS, LOOM, + AREA_SQM, GRADE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each roll that has a fabric in {{P}}FABRIC. Do not show a + roll when its fabric is not in {{P}}FABRIC. +2. FABRIC_NAME is the name of the fabric of the roll. +3. FIBRE_CLASS is 'N' when the fibre is cotton, wool or silk. FIBRE_CLASS is + 'S' for every other fibre. +4. LOOM is the first three characters of the roll ID. +5. AREA_SQM is LENGTH_CM multiplied by WIDTH_CM and divided by 10000. Round + the result to 2 decimals. +6. GRADE is 'A' when the roll has no defect. GRADE is 'B' when the roll has 1 + to 3 defects. GRADE is 'C' when the roll has more than 3 defects. + +# 6. Constraints +- Release target: 7.58. +- Out of scope: do not change {{P}}FABRIC and {{P}}ROLL. + +# 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/G1040/task.json b/tasks_gen/train/G1040/task.json new file mode 100644 index 0000000..8bb7a01 --- /dev/null +++ b/tasks_gen/train/G1040/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1040", + "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}}FABRIC", + "file": "seed/fabric.tabl.asabap", + "description": "Fabric master" + }, + { + "type": "TABL", + "name": "{{P}}ROLL", + "file": "seed/roll.tabl.asabap", + "description": "Fabric roll" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_ROLL_GRADE", + "fields": [ + "ROLL_ID", + "FABRIC_NAME", + "FIBRE_CLASS", + "LOOM", + "AREA_SQM", + "GRADE" + ] + } + ], + "out_of_scope": [ + "{{P}}FABRIC", + "{{P}}ROLL" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_ROLL_GRADE", + "file": "reference/i_roll_grade.ddls.asddls", + "description": "Fabric roll grade" + }, + { + "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/G1041/generation.json b/tasks_gen/train/G1041/generation.json new file mode 100644 index 0000000..42dba9a --- /dev/null +++ b/tasks_gen/train/G1041/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G1041", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 91.7, + "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/G1041/hidden/feed_hidden.clas.abap b/tasks_gen/train/G1041/hidden/feed_hidden.clas.abap new file mode 100644 index 0000000..3cbfb30 --- /dev/null +++ b/tasks_gen/train/G1041/hidden/feed_hidden.clas.abap @@ -0,0 +1,232 @@ +CLASS {{p}}feed_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_feed_stock. + + METHODS setup. + METHODS assert_stock + IMPORTING it_exp TYPE {{p}}if_feed_stock=>tt_stock + it_act TYPE {{p}}if_feed_stock=>tt_stock. + + METHODS empty_inputs FOR TESTING. + METHODS sums_one_supplier FOR TESTING. + METHODS merges_same_feed_type FOR TESTING. + METHODS ignores_empty_feed_type FOR TESTING. + METHODS ignores_zero_quantity FOR TESTING. + METHODS negative_reduces_total FOR TESTING. + METHODS removes_zero_total FOR TESTING. + METHODS result_sorted FOR TESTING. + METHODS unit_from_first_row FOR TESTING. + METHODS counts_deliveries FOR TESTING. + METHODS quantity_of_lookup FOR TESTING. + METHODS top_feed_and_tie FOR TESTING. +ENDCLASS. + + +CLASS {{p}}feed_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}feed_stock( ). + ENDMETHOD. + + METHOD assert_stock. + cl_abap_unit_assert=>assert_equals( exp = lines( it_exp ) + act = lines( it_act ) + msg = `row count` ). + DATA(lv_index) = 0. + LOOP AT it_exp ASSIGNING FIELD-SYMBOL(). + lv_index = lv_index + 1. + READ TABLE it_act ASSIGNING FIELD-SYMBOL() INDEX lv_index. + cl_abap_unit_assert=>assert_subrc( exp = 0 act = sy-subrc msg = `row missing` ). + cl_abap_unit_assert=>assert_equals( exp = -feed_type + act = -feed_type + msg = `feed type` ). + cl_abap_unit_assert=>assert_equals( exp = -unit + act = -unit + msg = `unit` ). + cl_abap_unit_assert=>assert_equals( exp = -quantity + act = -quantity + msg = `quantity` ). + cl_abap_unit_assert=>assert_equals( exp = -deliveries + act = -deliveries + msg = `deliveries` ). + ENDLOOP. + ENDMETHOD. + + METHOD empty_inputs. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + cl_abap_unit_assert=>assert_initial( lt_stock ). + ENDMETHOD. + + METHOD sums_one_supplier. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.500' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '4.500' ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '20.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '15.000' deliveries = 2 ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '20.000' deliveries = 1 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD merges_same_feed_type. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' ) + ( feed_type = 'FISH' unit = 'KG' quantity = '3.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'FISH' unit = 'KG' quantity = '3.000' deliveries = 1 ) + ( feed_type = 'HAY' unit = 'KG' quantity = '15.000' deliveries = 2 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD ignores_empty_feed_type. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = '' unit = 'KG' quantity = '100.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' deliveries = 1 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD ignores_zero_quantity. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'BOX' quantity = '0.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' deliveries = 1 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD negative_reduces_total. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '-4.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '6.000' deliveries = 2 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD removes_zero_total. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '-10.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '5.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '5.000' deliveries = 1 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD result_sorted. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '1.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '2.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'FISH' unit = 'KG' quantity = '3.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'FISH' unit = 'KG' quantity = '3.000' deliveries = 1 ) + ( feed_type = 'HAY' unit = 'KG' quantity = '2.000' deliveries = 1 ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '1.000' deliveries = 1 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD unit_from_first_row. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'BOX' quantity = '5.000' ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '2.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '7.000' ) + ( feed_type = 'PELLETS' unit = 'BOX' quantity = '3.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'BOX' quantity = '12.000' deliveries = 2 ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '5.000' deliveries = 2 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD counts_deliveries. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '1.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '1.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '1.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '-1.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '2.000' deliveries = 4 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD quantity_of_lookup. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.000' ) + ( feed_type = 'PELLETS' unit = 'KG' quantity = '4.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + + DATA lv_expected TYPE {{p}}if_feed_stock=>ty_quantity. + lv_expected = '10.000'. + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->quantity_of( it_stock = lt_stock iv_feed_type = 'HAY' ) + msg = `HAY` ). + + CLEAR lv_expected. + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->quantity_of( it_stock = lt_stock iv_feed_type = 'FISH' ) + msg = `FISH` ). + + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->quantity_of( + it_stock = VALUE {{p}}if_feed_stock=>tt_stock( ) + iv_feed_type = 'HAY' ) + msg = `empty list` ). + ENDMETHOD. + + METHOD top_feed_and_tie. + cl_abap_unit_assert=>assert_initial( + mo_cut->top_feed( it_stock = VALUE {{p}}if_feed_stock=>tt_stock( ) ) ). + + DATA(lt_stock) = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '9.000' deliveries = 1 ) + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' deliveries = 1 ) ). + DATA lv_expected TYPE {{p}}if_feed_stock=>ty_feed_type. + lv_expected = 'PELLETS'. + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->top_feed( it_stock = lt_stock ) ). + + DATA(lt_tie) = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '5.000' deliveries = 1 ) + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' deliveries = 1 ) ). + lv_expected = 'HAY'. + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->top_feed( it_stock = lt_tie ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1041/reference/feed_stock.clas.abap b/tasks_gen/train/G1041/reference/feed_stock.clas.abap new file mode 100644 index 0000000..b289107 --- /dev/null +++ b/tasks_gen/train/G1041/reference/feed_stock.clas.abap @@ -0,0 +1,90 @@ +CLASS {{p}}feed_stock DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_feed_stock. + + PRIVATE SECTION. + TYPES tt_sum TYPE HASHED TABLE OF {{p}}if_feed_stock=>ty_stock + WITH UNIQUE KEY feed_type. + + METHODS sum_deliveries + IMPORTING it_supplier_a TYPE {{p}}if_feed_stock=>tt_delivery + it_supplier_b TYPE {{p}}if_feed_stock=>tt_delivery + RETURNING VALUE(rt_sum) TYPE tt_sum. + + METHODS add_delivery + IMPORTING is_delivery TYPE {{p}}if_feed_stock=>ty_delivery + CHANGING ct_sum TYPE tt_sum. + + METHODS is_counted + IMPORTING is_delivery TYPE {{p}}if_feed_stock=>ty_delivery + RETURNING VALUE(rv_counted) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}feed_stock IMPLEMENTATION. + METHOD {{p}}if_feed_stock~consolidate. + DATA(lt_sum) = sum_deliveries( it_supplier_a = it_supplier_a + it_supplier_b = it_supplier_b ). + LOOP AT lt_sum ASSIGNING FIELD-SYMBOL(). + IF -quantity IS INITIAL. + CONTINUE. + ENDIF. + APPEND TO rt_stock. + ENDLOOP. + SORT rt_stock BY feed_type. + ENDMETHOD. + + METHOD {{p}}if_feed_stock~quantity_of. + DATA lt_stock TYPE {{p}}if_feed_stock=>tt_stock. + lt_stock = it_stock. + SORT lt_stock BY feed_type. + READ TABLE lt_stock ASSIGNING FIELD-SYMBOL() + WITH KEY feed_type = iv_feed_type BINARY SEARCH. + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD {{p}}if_feed_stock~top_feed. + IF it_stock IS INITIAL. + RETURN. + ENDIF. + DATA(lt_ranked) = it_stock. + SORT lt_ranked BY quantity DESCENDING feed_type ASCENDING. + rv_feed_type = lt_ranked[ 1 ]-feed_type. + ENDMETHOD. + + METHOD sum_deliveries. + DATA lt_all TYPE {{p}}if_feed_stock=>tt_delivery. + DATA lt_sum TYPE tt_sum. + APPEND LINES OF it_supplier_a TO lt_all. + APPEND LINES OF it_supplier_b TO lt_all. + LOOP AT lt_all ASSIGNING FIELD-SYMBOL(). + IF is_counted( ) = abap_false. + CONTINUE. + ENDIF. + add_delivery( EXPORTING is_delivery = + CHANGING ct_sum = lt_sum ). + ENDLOOP. + rt_sum = lt_sum. + ENDMETHOD. + + METHOD add_delivery. + ASSIGN ct_sum[ feed_type = is_delivery-feed_type ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( feed_type = is_delivery-feed_type + unit = is_delivery-unit + quantity = is_delivery-quantity + deliveries = 1 ) + INTO TABLE ct_sum ASSIGNING . + RETURN. + ENDIF. + -quantity = -quantity + is_delivery-quantity. + -deliveries = -deliveries + 1. + ENDMETHOD. + + METHOD is_counted. + rv_counted = xsdbool( is_delivery-feed_type IS NOT INITIAL + AND is_delivery-quantity IS NOT INITIAL ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1041/reference/feed_stock.testclasses.abap b/tasks_gen/train/G1041/reference/feed_stock.testclasses.abap new file mode 100644 index 0000000..b972fc0 --- /dev/null +++ b/tasks_gen/train/G1041/reference/feed_stock.testclasses.abap @@ -0,0 +1,85 @@ +CLASS ltc_feed_stock DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}feed_stock. + + METHODS setup. + METHODS assert_stock + IMPORTING it_exp TYPE {{p}}if_feed_stock=>tt_stock + it_act TYPE {{p}}if_feed_stock=>tt_stock. + + METHODS merges_two_suppliers FOR TESTING. + METHODS ignores_uncounted FOR TESTING. + METHODS removes_zero_total FOR TESTING. + METHODS quantity_of_missing FOR TESTING. + METHODS top_feed_tie FOR TESTING. +ENDCLASS. + + +CLASS ltc_feed_stock IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}feed_stock( ). + ENDMETHOD. + + METHOD assert_stock. + cl_abap_unit_assert=>assert_equals( exp = lines( it_exp ) act = lines( it_act ) ). + DATA(lv_index) = 0. + LOOP AT it_exp ASSIGNING FIELD-SYMBOL(). + lv_index = lv_index + 1. + READ TABLE it_act ASSIGNING FIELD-SYMBOL() INDEX lv_index. + cl_abap_unit_assert=>assert_subrc( exp = 0 act = sy-subrc ). + cl_abap_unit_assert=>assert_equals( exp = -feed_type act = -feed_type ). + cl_abap_unit_assert=>assert_equals( exp = -unit act = -unit ). + cl_abap_unit_assert=>assert_equals( exp = -quantity act = -quantity ). + cl_abap_unit_assert=>assert_equals( exp = -deliveries act = -deliveries ). + ENDLOOP. + ENDMETHOD. + + METHOD merges_two_suppliers. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '10.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' ) ) ). + assert_stock( + it_exp = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'HAY' unit = 'KG' quantity = '15.000' deliveries = 2 ) ) + it_act = lt_stock ). + ENDMETHOD. + + METHOD ignores_uncounted. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = '' unit = 'KG' quantity = '9.000' ) + ( feed_type = 'FISH' unit = 'KG' quantity = '0.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + cl_abap_unit_assert=>assert_initial( lt_stock ). + ENDMETHOD. + + METHOD removes_zero_total. + DATA(lt_stock) = mo_cut->consolidate( + it_supplier_a = VALUE {{p}}if_feed_stock=>tt_delivery( + ( feed_type = 'HAY' unit = 'KG' quantity = '4.000' ) + ( feed_type = 'HAY' unit = 'KG' quantity = '-4.000' ) ) + it_supplier_b = VALUE {{p}}if_feed_stock=>tt_delivery( ) ). + cl_abap_unit_assert=>assert_initial( lt_stock ). + ENDMETHOD. + + METHOD quantity_of_missing. + DATA lv_expected TYPE {{p}}if_feed_stock=>ty_quantity. + cl_abap_unit_assert=>assert_equals( + exp = lv_expected + act = mo_cut->quantity_of( + it_stock = VALUE {{p}}if_feed_stock=>tt_stock( ) + iv_feed_type = 'HAY' ) ). + ENDMETHOD. + + METHOD top_feed_tie. + DATA(lt_stock) = VALUE {{p}}if_feed_stock=>tt_stock( + ( feed_type = 'PELLETS' unit = 'KG' quantity = '5.000' deliveries = 1 ) + ( feed_type = 'HAY' unit = 'KG' quantity = '5.000' deliveries = 1 ) ). + DATA lv_expected TYPE {{p}}if_feed_stock=>ty_feed_type. + lv_expected = 'HAY'. + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = mo_cut->top_feed( it_stock = lt_stock ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1041/seed/if_feed_stock.intf.abap b/tasks_gen/train/G1041/seed/if_feed_stock.intf.abap new file mode 100644 index 0000000..8d2124e --- /dev/null +++ b/tasks_gen/train/G1041/seed/if_feed_stock.intf.abap @@ -0,0 +1,33 @@ +INTERFACE {{p}}if_feed_stock PUBLIC. + TYPES: + ty_feed_type TYPE c LENGTH 8, + ty_unit TYPE c LENGTH 3, + ty_quantity TYPE p LENGTH 13 DECIMALS 3, + BEGIN OF ty_delivery, + feed_type TYPE ty_feed_type, + unit TYPE ty_unit, + quantity TYPE ty_quantity, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_stock, + feed_type TYPE ty_feed_type, + unit TYPE ty_unit, + quantity TYPE ty_quantity, + deliveries TYPE i, + END OF ty_stock, + tt_stock TYPE STANDARD TABLE OF ty_stock WITH EMPTY KEY. + + METHODS consolidate + IMPORTING it_supplier_a TYPE tt_delivery + it_supplier_b TYPE tt_delivery + RETURNING VALUE(rt_stock) TYPE tt_stock. + + METHODS quantity_of + IMPORTING it_stock TYPE tt_stock + iv_feed_type TYPE ty_feed_type + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + + METHODS top_feed + IMPORTING it_stock TYPE tt_stock + RETURNING VALUE(rv_feed_type) TYPE ty_feed_type. +ENDINTERFACE. diff --git a/tasks_gen/train/G1041/spec.md b/tasks_gen/train/G1041/spec.md new file mode 100644 index 0000000..f760ce7 --- /dev/null +++ b/tasks_gen/train/G1041/spec.md @@ -0,0 +1,76 @@ +# 1. Goal +A zoo keeps a feed store. Two suppliers send their feed deliveries. The store +manager needs one consolidated stock list per feed type, the quantity of one +feed type, and the feed type with the highest quantity. A store program calls +the class for each delivery round. + +# 2. Open questions +None. + +# 3. Context +- Interface {{P}}IF_FEED_STOCK exists in package $TMP. It is active. +- The interface has these types: + - TY_FEED_TYPE (CHAR 8), TY_UNIT (CHAR 3), TY_QUANTITY (packed, 3 decimals). + - TY_DELIVERY with the fields FEED_TYPE, UNIT and QUANTITY. + - TT_DELIVERY: a table of TY_DELIVERY. + - TY_STOCK with the fields FEED_TYPE, UNIT, QUANTITY and DELIVERIES + (integer). + - TT_STOCK: a table of TY_STOCK. +- The interface has three methods: + - CONSOLIDATE with the importing parameters IT_SUPPLIER_A and IT_SUPPLIER_B + (both TT_DELIVERY) and the returning parameter RT_STOCK (TT_STOCK). + - QUANTITY_OF with the importing parameters IT_STOCK (TT_STOCK) and + IV_FEED_TYPE (TY_FEED_TYPE) and the returning parameter RV_QUANTITY + (TY_QUANTITY). + - TOP_FEED with the importing parameter IT_STOCK (TT_STOCK) and the + returning parameter RV_FEED_TYPE (TY_FEED_TYPE). + +# 4. Contract +- Create the class {{P}}FEED_STOCK in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_FEED_STOCK. +- Do not add other public methods. + +# 5. Business rules + +## 5.1 Input +- A delivery row has the fields FEED_TYPE, UNIT and QUANTITY. +- The caller passes two delivery lists: supplier A and supplier B. Each list + can hold any number of rows, including none. + +## 5.2 CONSOLIDATE +1. Count a row only when its feed type is not empty and its quantity is not + zero. Ignore every other row. +2. Read the counted rows of supplier A first, in the given order, then the + counted rows of supplier B, in the given order. +3. Group the counted rows by feed type. The result has one row per feed type. +4. The quantity of a result row is the sum of the quantities of the counted + rows of this feed type. A negative quantity reduces the sum. +5. The unit of a result row is the unit of the first counted row of this feed + type. +6. The deliveries of a result row is the number of counted rows of this feed + type. +7. Remove a result row whose quantity is zero. +8. Sort the result rows by feed type in ascending order. +9. Return an empty list when no result row remains. + +## 5.3 QUANTITY_OF +1. Return the quantity of the result row with the given feed type. +2. Return zero when the list has no row with this feed type. + +## 5.4 TOP_FEED +1. Return the feed type with the highest quantity in the list. +2. When more than one feed type has the same highest quantity, return the feed + type that comes first in ascending order. +3. Return an empty feed type when the list is empty. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_FEED_STOCK. + +# 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/G1041/task.json b/tasks_gen/train/G1041/task.json new file mode 100644 index 0000000..12efbe1 --- /dev/null +++ b/tasks_gen/train/G1041/task.json @@ -0,0 +1,52 @@ +{ + "id": "G1041", + "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_FEED_STOCK", + "file": "seed/if_feed_stock.intf.abap", + "description": "Feed stock contract with the delivery and stock types" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}FEED_STOCK", + "implements": "{{P}}IF_FEED_STOCK" + } + ], + "out_of_scope": [ + "{{P}}IF_FEED_STOCK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}FEED_HIDDEN", + "file": "hidden/feed_hidden.clas.abap", + "description": "T41 hidden tests for the feed stock consolidation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}FEED_STOCK", + "file": "reference/feed_stock.clas.abap", + "description": "Feed stock consolidation, lookup and top feed", + "testclasses_file": "reference/feed_stock.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loop", + "linear_search_in_loop" + ] +} \ 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 new file mode 100644 index 0000000..a8fe40c --- /dev/null +++ b/tasks_gen/train/G1042/hidden/snow_fee_hidden.clas.abap @@ -0,0 +1,90 @@ +CLASS {{p}}snow_fee_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}snow_fee. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_area TYPE {{p}}snow_fee=>ty_area + iv_priority TYPE {{p}}snow_fee=>ty_priority + iv_distance TYPE {{p}}snow_fee=>ty_distance + iv_expected TYPE {{p}}snow_fee=>ty_fee. + + METHODS minimum_charge FOR TESTING. + METHODS small_area_rate FOR TESTING. + METHODS mid_area_rate FOR TESTING. + METHODS mid_area_upper FOR TESTING. + METHODS large_area_rate FOR TESTING. + METHODS express_surcharge FOR TESTING. + METHODS emergency_surcharge FOR TESTING. + METHODS unknown_priority FOR TESTING. + METHODS minimum_with_express FOR TESTING. + METHODS travel_charged FOR TESTING. + METHODS travel_not_surcharged FOR TESTING. + METHODS zero_area_returns_zero FOR TESTING. +ENDCLASS. + + +CLASS {{p}}snow_fee_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}snow_fee( ). + 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 + act = mo_cut->calculate( iv_area = iv_area + iv_priority = iv_priority + iv_distance = iv_distance ) ). + ENDMETHOD. + + METHOD minimum_charge. + assert_fee( iv_area = 100 iv_priority = 'S' iv_distance = 0 iv_expected = '45.00' ). + ENDMETHOD. + + METHOD small_area_rate. + 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' ). + ENDMETHOD. + + METHOD mid_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' ). + ENDMETHOD. + + METHOD express_surcharge. + assert_fee( iv_area = 1000 iv_priority = 'E' iv_distance = 0 iv_expected = '200.00' ). + ENDMETHOD. + + METHOD emergency_surcharge. + assert_fee( iv_area = 1000 iv_priority = 'X' iv_distance = 0 iv_expected = '240.00' ). + ENDMETHOD. + + METHOD unknown_priority. + assert_fee( iv_area = 1000 iv_priority = 'Z' iv_distance = 0 iv_expected = '160.00' ). + ENDMETHOD. + + METHOD minimum_with_express. + assert_fee( iv_area = 100 iv_priority = 'E' iv_distance = 0 iv_expected = '56.25' ). + ENDMETHOD. + + METHOD travel_charged. + assert_fee( iv_area = 1000 iv_priority = 'S' iv_distance = 30 iv_expected = '168.00' ). + ENDMETHOD. + + METHOD travel_not_surcharged. + assert_fee( iv_area = 1000 iv_priority = 'E' iv_distance = 30 iv_expected = '208.00' ). + ENDMETHOD. + + METHOD zero_area_returns_zero. + assert_fee( iv_area = 0 iv_priority = 'X' iv_distance = 30 iv_expected = '0.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/reference/snow_fee.clas.abap b/tasks_gen/train/G1042/reference/snow_fee.clas.abap new file mode 100644 index 0000000..b02e96d --- /dev/null +++ b/tasks_gen/train/G1042/reference/snow_fee.clas.abap @@ -0,0 +1,86 @@ +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'. + + 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) = 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 <= 500. + rv_rate = c_rate_small. + ELSEIF iv_area <= 2000. + 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 = 125. + WHEN c_priority_emerg. + rv_factor = 150. + WHEN OTHERS. + rv_factor = 100. + ENDCASE. + ENDMETHOD. + + METHOD travel_cost. + DATA(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/reference/snow_fee.testclasses.abap b/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap new file mode 100644 index 0000000..e28a470 --- /dev/null +++ b/tasks_gen/train/G1042/reference/snow_fee.testclasses.abap @@ -0,0 +1,59 @@ +CLASS ltc_snow_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}snow_fee. + + METHODS setup. + METHODS assert_fee + IMPORTING iv_area TYPE {{p}}snow_fee=>ty_area + iv_priority TYPE {{p}}snow_fee=>ty_priority + iv_distance TYPE {{p}}snow_fee=>ty_distance + iv_expected TYPE {{p}}snow_fee=>ty_fee. + + METHODS minimum_charge FOR TESTING. + METHODS small_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. + + +CLASS ltc_snow_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}snow_fee( ). + 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 + act = mo_cut->calculate( iv_area = iv_area + iv_priority = iv_priority + iv_distance = iv_distance ) ). + ENDMETHOD. + + METHOD minimum_charge. + assert_fee( iv_area = 100 iv_priority = 'S' iv_distance = 0 iv_expected = '45.00' ). + ENDMETHOD. + + METHOD small_area_rate. + assert_fee( iv_area = 500 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. + + METHOD travel_charged. + assert_fee( iv_area = 1000 iv_priority = 'S' iv_distance = 30 iv_expected = '168.00' ). + ENDMETHOD. + + 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 new file mode 100644 index 0000000..04ce9ea --- /dev/null +++ b/tasks_gen/train/G1042/seed/snow_fee.clas.abap @@ -0,0 +1,64 @@ +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. +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, + lv_km TYPE ty_distance. + + rv_fee = 0. + + IF iv_area > 0. + + IF iv_area <= 500. + lv_rate = '0.20'. + ELSE. + IF iv_area <= 2000. + lv_rate = '0.16'. + ELSE. + lv_rate = '0.12'. + ENDIF. + ENDIF. + + lv_base = iv_area * lv_rate. + + IF lv_base < 45. + lv_base = 45. + ENDIF. + + IF iv_priority = 'E'. + lv_base = lv_base * 125 / 100. + ELSE. + IF iv_priority = 'X'. + lv_base = lv_base * 150 / 100. + ENDIF. + ENDIF. + + lv_sum = lv_base. + + lv_km = iv_distance - 10. + IF lv_km > 0. + lv_rate = '0.40'. + lv_sum = lv_sum + lv_km * lv_rate. + ENDIF. + + rv_fee = lv_sum. + + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1042/spec.md b/tasks_gen/train/G1042/spec.md new file mode 100644 index 0000000..a752ae4 --- /dev/null +++ b/tasks_gen/train/G1042/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A winter service clears snow from the roads of its clients. The dispatcher calls +the class {{P}}SNOW_FEE for each snow clearing job. The class is in package $TMP +and is active. The code is legacy code: one long method, deep nesting and +hardcoded numbers. Refactor the class in place. The class name, the public types +and the public method stay the same. The behavior stays the same. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}SNOW_FEE is in package $TMP. It is active. +- The dispatcher calls the method CALCULATE for each job. +- The dispatcher gives the area of the site in square metres, the priority of the + job and the distance to the site in kilometres. +- The class is legacy code. No user reports an error. This is a refactoring + task: the result of the method must not change. + +# 4. Contract +- The class {{P}}SNOW_FEE stays in package $TMP. Do not rename it. +- The class is public and final. It has a public constructor without parameters. +- The public section keeps these types: + - TY_AREA (TYPE i) + - TY_PRIORITY (TYPE c LENGTH 1) + - TY_DISTANCE (TYPE i) + - TY_FEE (TYPE p LENGTH 9 DECIMALS 2) +- The public section keeps one method: + CALCULATE + IMPORTING iv_area TYPE ty_area + iv_priority TYPE ty_priority + iv_distance TYPE ty_distance + RETURNING VALUE(rv_fee) TYPE ty_fee. +- Do not add other public methods. Do not change the signature. + +# 5. Business rules +- The behavior of the class must stay exactly the same. The current + implementation is the only source of the rules. +- Read the legacy code before you change it. Keep every threshold, every + surcharge, every minimum and every special case. +- The hidden tests call the public method with the same input as the legacy code + and compare the result. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standards: Clean ABAP. Each method below 40 statements. No deep + nesting. Use constants for the numbers. No comment that restates the code. +- Out of scope: do not change the public interface; 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/G1042/task.json b/tasks_gen/train/G1042/task.json new file mode 100644 index 0000000..c65da3a --- /dev/null +++ b/tasks_gen/train/G1042/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1042", + "category": "E", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}SNOW_FEE", + "file": "seed/snow_fee.clas.abap", + "description": "Legacy class that calculates the fee of one snow clearing job" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SNOW_FEE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}SNOW_FEE_HIDDEN", + "file": "hidden/snow_fee_hidden.clas.abap", + "description": "Hidden tests for the unchanged behavior of {{P}}SNOW_FEE" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SNOW_FEE", + "file": "reference/snow_fee.clas.abap", + "description": "Refactored snow clearing fee calculation", + "testclasses_file": "reference/snow_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1043/faulty/m0_med_copay.clas.abap b/tasks_gen/train/G1043/faulty/m0_med_copay.clas.abap new file mode 100644 index 0000000..7b6c29e --- /dev/null +++ b/tasks_gen/train/G1043/faulty/m0_med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity >= 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_false. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total * iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + OR iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier = {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/faulty/m1_med_copay.clas.abap b/tasks_gen/train/G1043/faulty/m1_med_copay.clas.abap new file mode 100644 index 0000000..92753b1 --- /dev/null +++ b/tasks_gen/train/G1043/faulty/m1_med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity < 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_true. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total * iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + OR iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier = {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/faulty/m2_med_copay.clas.abap b/tasks_gen/train/G1043/faulty/m2_med_copay.clas.abap new file mode 100644 index 0000000..e6118e6 --- /dev/null +++ b/tasks_gen/train/G1043/faulty/m2_med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity < 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_false. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total / iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + OR iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier = {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/faulty/m3_med_copay.clas.abap b/tasks_gen/train/G1043/faulty/m3_med_copay.clas.abap new file mode 100644 index 0000000..e0b5d12 --- /dev/null +++ b/tasks_gen/train/G1043/faulty/m3_med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity < 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_false. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total * iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + AND iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier = {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/faulty/m4_med_copay.clas.abap b/tasks_gen/train/G1043/faulty/m4_med_copay.clas.abap new file mode 100644 index 0000000..bc1a568 --- /dev/null +++ b/tasks_gen/train/G1043/faulty/m4_med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity < 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_false. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total * iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + OR iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier <> {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/generation.json b/tasks_gen/train/G1043/generation.json new file mode 100644 index 0000000..8838a70 --- /dev/null +++ b/tasks_gen/train/G1043/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1043", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "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/G1043/hidden/t40_hidden.clas.abap b/tasks_gen/train/G1043/hidden/t40_hidden.clas.abap new file mode 100644 index 0000000..0439efc --- /dev/null +++ b/tasks_gen/train/G1043/hidden/t40_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}t40_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_med_copay. + METHODS setup. + METHODS standard_minimum FOR TESTING. + METHODS standard_percentage FOR TESTING. + METHODS standard_cap FOR TESTING. + METHODS chronic_percentage FOR TESTING. + METHODS chronic_cap FOR TESTING. + METHODS exempt_pays_nothing FOR TESTING. + METHODS quantity_and_rounding FOR TESTING. + METHODS copay_not_above_total FOR TESTING. + METHODS rejects_zero_price FOR TESTING. + METHODS rejects_zero_quantity FOR TESTING. + METHODS rejects_unknown_tier FOR TESTING. + METHODS price_rule_comes_first FOR TESTING. + METHODS assert_case + IMPORTING iv_price TYPE {{p}}if_med_copay=>ty_amount + iv_quantity TYPE i + iv_tier TYPE {{p}}if_med_copay=>ty_tier + iv_exp_copay TYPE {{p}}if_med_copay=>ty_amount + iv_exp_reason TYPE {{p}}if_med_copay=>ty_reason + iv_msg TYPE string DEFAULT ''. +ENDCLASS. + + +CLASS {{p}}t40_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}med_copay( ). + ENDMETHOD. + + METHOD standard_minimum. + assert_case( iv_price = '10.00' iv_quantity = 1 iv_tier = 'S' + iv_exp_copay = '5.00' iv_exp_reason = 'OK' iv_msg = `standard minimum` ). + ENDMETHOD. + + METHOD standard_percentage. + assert_case( iv_price = '100.00' iv_quantity = 1 iv_tier = 'S' + iv_exp_copay = '30.00' iv_exp_reason = 'OK' iv_msg = `standard percentage` ). + ENDMETHOD. + + METHOD standard_cap. + assert_case( iv_price = '500.00' iv_quantity = 1 iv_tier = 'S' + iv_exp_copay = '50.00' iv_exp_reason = 'OK' iv_msg = `standard cap` ). + ENDMETHOD. + + METHOD chronic_percentage. + assert_case( iv_price = '66.67' iv_quantity = 1 iv_tier = 'C' + iv_exp_copay = '6.67' iv_exp_reason = 'OK' iv_msg = `chronic percentage` ). + ENDMETHOD. + + METHOD chronic_cap. + assert_case( iv_price = '100.00' iv_quantity = 2 iv_tier = 'C' + iv_exp_copay = '10.00' iv_exp_reason = 'OK' iv_msg = `chronic cap` ). + ENDMETHOD. + + METHOD exempt_pays_nothing. + assert_case( iv_price = '100.00' iv_quantity = 3 iv_tier = 'E' + iv_exp_copay = '0.00' iv_exp_reason = 'OK' iv_msg = `exempt` ). + ENDMETHOD. + + METHOD quantity_and_rounding. + assert_case( iv_price = '8.33' iv_quantity = 3 iv_tier = 'S' + iv_exp_copay = '7.50' iv_exp_reason = 'OK' iv_msg = `rounding` ). + ENDMETHOD. + + METHOD copay_not_above_total. + assert_case( iv_price = '2.00' iv_quantity = 1 iv_tier = 'S' + iv_exp_copay = '2.00' iv_exp_reason = 'OK' iv_msg = `total cap` ). + ENDMETHOD. + + METHOD rejects_zero_price. + assert_case( iv_price = '0.00' iv_quantity = 1 iv_tier = 'S' + iv_exp_copay = '0.00' iv_exp_reason = 'PRICE' iv_msg = `zero price` ). + ENDMETHOD. + + METHOD rejects_zero_quantity. + assert_case( iv_price = '10.00' iv_quantity = 0 iv_tier = 'S' + iv_exp_copay = '0.00' iv_exp_reason = 'QUANTITY' iv_msg = `zero quantity` ). + ENDMETHOD. + + METHOD rejects_unknown_tier. + assert_case( iv_price = '10.00' iv_quantity = 1 iv_tier = 'X' + iv_exp_copay = '0.00' iv_exp_reason = 'TIER' iv_msg = `unknown tier` ). + ENDMETHOD. + + METHOD price_rule_comes_first. + assert_case( iv_price = '0.00' iv_quantity = 0 iv_tier = 'X' + iv_exp_copay = '0.00' iv_exp_reason = 'PRICE' iv_msg = `rule order` ). + ENDMETHOD. + + METHOD assert_case. + DATA(ls_result) = mo_cut->calculate( iv_price = iv_price + iv_quantity = iv_quantity + iv_tier = iv_tier ). + cl_abap_unit_assert=>assert_equals( exp = iv_exp_reason act = ls_result-reason msg = iv_msg ). + cl_abap_unit_assert=>assert_equals( exp = iv_exp_copay act = ls_result-copay msg = iv_msg ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/mutation.json b/tasks_gen/train/G1043/mutation.json new file mode 100644 index 0000000..997b05e --- /dev/null +++ b/tasks_gen/train/G1043/mutation.json @@ -0,0 +1,82 @@ +{ + "task": "G1043", + "mutants": [ + { + "object": "{{P}}MED_COPAY", + "mutant": "line 39: < -> >= (rel)", + "status": "killed", + "hidden": "2/12", + "failed_tests": [ + "CHRONIC_CAP", + "CHRONIC_PERCENTAGE", + "COPAY_NOT_ABOVE_TOTAL", + "EXEMPT_PAYS_NOTHING", + "QUANTITY_AND_ROUNDING", + "REJECTS_UNKNOWN_TIER", + "REJECTS_ZERO_QUANTITY", + "STANDARD_CAP", + "STANDARD_MINIMUM", + "STANDARD_PERCENTAGE" + ] + }, + { + "object": "{{P}}MED_COPAY", + "mutant": "line 43: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "CHRONIC_CAP", + "CHRONIC_PERCENTAGE", + "COPAY_NOT_ABOVE_TOTAL", + "EXEMPT_PAYS_NOTHING", + "QUANTITY_AND_ROUNDING", + "REJECTS_UNKNOWN_TIER", + "STANDARD_CAP", + "STANDARD_MINIMUM", + "STANDARD_PERCENTAGE" + ] + }, + { + "object": "{{P}}MED_COPAY", + "mutant": "line 76: * -> / (arith)", + "status": "killed", + "hidden": "7/12", + "failed_tests": [ + "CHRONIC_CAP", + "CHRONIC_PERCENTAGE", + "QUANTITY_AND_ROUNDING", + "STANDARD_CAP", + "STANDARD_PERCENTAGE" + ] + }, + { + "object": "{{P}}MED_COPAY", + "mutant": "line 90: OR -> AND (logic)", + "status": "killed", + "hidden": "5/12", + "failed_tests": [ + "CHRONIC_CAP", + "CHRONIC_PERCENTAGE", + "COPAY_NOT_ABOVE_TOTAL", + "QUANTITY_AND_ROUNDING", + "STANDARD_CAP", + "STANDARD_MINIMUM", + "STANDARD_PERCENTAGE" + ] + }, + { + "object": "{{P}}MED_COPAY", + "mutant": "line 91: = -> <> (eq)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "EXEMPT_PAYS_NOTHING", + "REJECTS_UNKNOWN_TIER" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1043/reference/med_copay.clas.abap b/tasks_gen/train/G1043/reference/med_copay.clas.abap new file mode 100644 index 0000000..3c998be --- /dev/null +++ b/tasks_gen/train/G1043/reference/med_copay.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}med_copay DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_med_copay. + + PRIVATE SECTION. + CONSTANTS: + c_percent_standard TYPE i VALUE 30, + c_percent_chronic TYPE i VALUE 10, + c_min_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_standard TYPE {{p}}if_med_copay=>ty_amount VALUE '50.00', + c_min_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '5.00', + c_max_chronic TYPE {{p}}if_med_copay=>ty_amount VALUE '10.00'. + + METHODS copay_for_tier + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS percentage + IMPORTING iv_total TYPE {{p}}if_med_copay=>ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS apply_limits + IMPORTING iv_amount TYPE {{p}}if_med_copay=>ty_amount + iv_min TYPE {{p}}if_med_copay=>ty_amount + iv_max TYPE {{p}}if_med_copay=>ty_amount + RETURNING VALUE(rv_amount) TYPE {{p}}if_med_copay=>ty_amount. + METHODS is_known_tier + IMPORTING iv_tier TYPE {{p}}if_med_copay=>ty_tier + RETURNING VALUE(rv_known) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}med_copay IMPLEMENTATION. + METHOD {{p}}if_med_copay~calculate. + IF iv_price <= 0. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-price ). + RETURN. + ENDIF. + IF iv_quantity < 1. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-quantity ). + RETURN. + ENDIF. + IF is_known_tier( iv_tier ) = abap_false. + rs_result = VALUE #( copay = 0 reason = {{p}}if_med_copay=>c_reason-tier ). + RETURN. + ENDIF. + DATA lv_total TYPE {{p}}if_med_copay=>ty_amount. + lv_total = iv_price * iv_quantity. + rs_result = VALUE #( copay = copay_for_tier( iv_total = lv_total iv_tier = iv_tier ) + reason = {{p}}if_med_copay=>c_reason-ok ). + IF rs_result-copay > lv_total. + rs_result-copay = lv_total. + ENDIF. + ENDMETHOD. + + METHOD copay_for_tier. + CASE iv_tier. + WHEN {{p}}if_med_copay=>c_tier-exempt. + rv_amount = 0. + WHEN {{p}}if_med_copay=>c_tier-chronic. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_chronic ) + iv_min = c_min_chronic + iv_max = c_max_chronic ). + WHEN {{p}}if_med_copay=>c_tier-standard. + rv_amount = apply_limits( + iv_amount = percentage( iv_total = iv_total iv_percent = c_percent_standard ) + iv_min = c_min_standard + iv_max = c_max_standard ). + WHEN OTHERS. + rv_amount = 0. + ENDCASE. + ENDMETHOD. + + METHOD percentage. + rv_amount = round( val = iv_total * iv_percent / 100 dec = 2 ). + ENDMETHOD. + + METHOD apply_limits. + rv_amount = iv_amount. + IF rv_amount < iv_min. + rv_amount = iv_min. + ELSEIF rv_amount > iv_max. + rv_amount = iv_max. + ENDIF. + ENDMETHOD. + + METHOD is_known_tier. + rv_known = xsdbool( iv_tier = {{p}}if_med_copay=>c_tier-standard + OR iv_tier = {{p}}if_med_copay=>c_tier-chronic + OR iv_tier = {{p}}if_med_copay=>c_tier-exempt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/reference/med_copay.testclasses.abap b/tasks_gen/train/G1043/reference/med_copay.testclasses.abap new file mode 100644 index 0000000..f4ab44c --- /dev/null +++ b/tasks_gen/train/G1043/reference/med_copay.testclasses.abap @@ -0,0 +1,73 @@ +CLASS ltc_med_copay DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_med_copay. + METHODS setup. + METHODS standard_pays_minimum FOR TESTING. + METHODS standard_pays_percentage FOR TESTING. + METHODS chronic_pays_percentage FOR TESTING. + METHODS exempt_pays_nothing FOR TESTING. + METHODS copay_is_capped_at_total FOR TESTING. + METHODS zero_price_is_rejected FOR TESTING. + METHODS zero_quantity_is_rejected FOR TESTING. + METHODS unknown_tier_is_rejected FOR TESTING. +ENDCLASS. + + +CLASS ltc_med_copay IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}med_copay( ). + ENDMETHOD. + + METHOD standard_pays_minimum. + DATA(ls_result) = mo_cut->calculate( iv_price = '10.00' iv_quantity = 1 iv_tier = 'S' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_med_copay=>ty_amount( '5.00' ) + act = ls_result-copay ). + ENDMETHOD. + + METHOD standard_pays_percentage. + DATA(ls_result) = mo_cut->calculate( iv_price = '100.00' iv_quantity = 1 iv_tier = 'S' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_med_copay=>ty_amount( '30.00' ) + act = ls_result-copay ). + ENDMETHOD. + + METHOD chronic_pays_percentage. + DATA(ls_result) = mo_cut->calculate( iv_price = '66.67' iv_quantity = 1 iv_tier = 'C' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_med_copay=>ty_amount( '6.67' ) + act = ls_result-copay ). + ENDMETHOD. + + METHOD exempt_pays_nothing. + DATA(ls_result) = mo_cut->calculate( iv_price = '100.00' iv_quantity = 3 iv_tier = 'E' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_med_copay=>ty_amount( '0.00' ) + act = ls_result-copay ). + ENDMETHOD. + + METHOD copay_is_capped_at_total. + DATA(ls_result) = mo_cut->calculate( iv_price = '2.00' iv_quantity = 1 iv_tier = 'S' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}if_med_copay=>ty_amount( '2.00' ) + act = ls_result-copay ). + ENDMETHOD. + + METHOD zero_price_is_rejected. + DATA(ls_result) = mo_cut->calculate( iv_price = '0.00' iv_quantity = 2 iv_tier = 'S' ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_med_copay=>c_reason-price + act = ls_result-reason ). + ENDMETHOD. + + METHOD zero_quantity_is_rejected. + DATA(ls_result) = mo_cut->calculate( iv_price = '10.00' iv_quantity = 0 iv_tier = 'C' ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_med_copay=>c_reason-quantity + act = ls_result-reason ). + ENDMETHOD. + + METHOD unknown_tier_is_rejected. + DATA(ls_result) = mo_cut->calculate( iv_price = '10.00' iv_quantity = 1 iv_tier = 'Z' ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_med_copay=>c_reason-tier + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1043/seed/if_med_copay.intf.abap b/tasks_gen/train/G1043/seed/if_med_copay.intf.abap new file mode 100644 index 0000000..23f2ddc --- /dev/null +++ b/tasks_gen/train/G1043/seed/if_med_copay.intf.abap @@ -0,0 +1,32 @@ +INTERFACE {{p}}if_med_copay PUBLIC. + TYPES ty_tier TYPE c LENGTH 1. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_tier, + standard TYPE ty_tier VALUE 'S', + chronic TYPE ty_tier VALUE 'C', + exempt TYPE ty_tier VALUE 'E', + END OF c_tier. + + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + price TYPE ty_reason VALUE 'PRICE', + quantity TYPE ty_reason VALUE 'QUANTITY', + tier TYPE ty_reason VALUE 'TIER', + END OF c_reason. + + TYPES: + BEGIN OF ty_result, + copay TYPE ty_amount, + reason TYPE ty_reason, + END OF ty_result. + + METHODS calculate + IMPORTING iv_price TYPE ty_amount + iv_quantity TYPE i + iv_tier TYPE ty_tier + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/train/G1043/spec.md b/tasks_gen/train/G1043/spec.md new file mode 100644 index 0000000..5550caa --- /dev/null +++ b/tasks_gen/train/G1043/spec.md @@ -0,0 +1,58 @@ +# 1. Goal +A pharmacy calculates the co-payment that a customer pays for a prescription. +A checkout program calls the calculation before it prints the receipt. The +program needs the co-payment amount and one reason code for each call. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_MED_COPAY exists in package $TMP. It is active. +- The interface has the types TY_TIER (character, length 1), TY_REASON + (character, length 10) and TY_AMOUNT (packed number, length 9, 2 decimals). +- The interface has the constant structure C_TIER with the values STANDARD, + CHRONIC and EXEMPT. +- The interface has the constant structure C_REASON with the values OK, PRICE, + QUANTITY and TIER. +- The interface has the structure TY_RESULT with the components COPAY (TY_AMOUNT) + and REASON (TY_REASON). +- The interface has one method: + CALCULATE + IMPORTING iv_price TYPE ty_amount + iv_quantity TYPE i + iv_tier TYPE ty_tier + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}MED_COPAY in package $TMP. +- The class is public, final and has a public constructor without parameters. +- The class implements {{P}}IF_MED_COPAY. +- 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. If iv_price is zero or less, return the reason PRICE and the co-payment 0. +2. If iv_quantity is less than 1, return the reason QUANTITY and the co-payment 0. +3. If iv_tier is not STANDARD, CHRONIC or EXEMPT, return the reason TIER and the + co-payment 0. +4. The total price is iv_price multiplied by iv_quantity. +5. If iv_tier is EXEMPT, the co-payment is 0. Continue with rule 9. +6. If iv_tier is CHRONIC, the co-payment is 10 per cent of the total price. +7. If iv_tier is STANDARD, the co-payment is 30 per cent of the total price. +8. Round the co-payment to 2 decimals (commercial rounding, half away from zero). + Then apply the limits. For CHRONIC the minimum is 5.00 and the maximum is + 10.00. For STANDARD the minimum is 5.00 and the maximum is 50.00. +9. If the co-payment is more than the total price, the co-payment is the total + price. +10. Return the reason 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_MED_COPAY. + +# 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/G1043/task.json b/tasks_gen/train/G1043/task.json new file mode 100644 index 0000000..f29045d --- /dev/null +++ b/tasks_gen/train/G1043/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1043", + "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_MED_COPAY", + "file": "seed/if_med_copay.intf.abap", + "description": "Co-payment contract with types, constants and the CALCULATE method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}MED_COPAY", + "implements": "{{P}}IF_MED_COPAY" + } + ], + "out_of_scope": [ + "{{P}}IF_MED_COPAY" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T40_HIDDEN", + "file": "hidden/t40_hidden.clas.abap", + "description": "T40 hidden tests for the co-payment calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}MED_COPAY", + "file": "reference/med_copay.clas.abap", + "description": "Prescription co-payment calculator", + "testclasses_file": "reference/med_copay.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1044/faulty/m0_escape_checker.clas.abap b/tasks_gen/train/G1044/faulty/m0_escape_checker.clas.abap new file mode 100644 index 0000000..ed6db91 --- /dev/null +++ b/tasks_gen/train/G1044/faulty/m0_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 = 17 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/m1_escape_checker.clas.abap b/tasks_gen/train/G1044/faulty/m1_escape_checker.clas.abap new file mode 100644 index 0000000..62f9f04 --- /dev/null +++ b/tasks_gen/train/G1044/faulty/m1_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 = 36 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/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1044/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..a96790e --- /dev/null +++ b/tasks_gen/train/G1044/hidden/t02_hidden.clas.abap @@ -0,0 +1,161 @@ +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}}escape_checker. + + METHODS setup. + METHODS price_mars_ok FOR TESTING. + METHODS price_group_discount FOR TESTING. + METHODS price_deep_ok FOR TESTING. + METHODS price_bank_ok FOR TESTING. + METHODS price_jung_ok FOR TESTING. + METHODS unknown_room FOR TESTING. + METHODS empty_room FOR TESTING. + METHODS lower_case_room FOR TESTING. + METHODS too_few_players FOR TESTING. + METHODS too_many_players FOR TESTING. + METHODS too_young FOR TESTING. + METHODS lead_time_too_short FOR TESTING. + + METHODS assert_price + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i + iv_exp TYPE {{p}}escape_checker=>ty_price. + METHODS assert_room_error + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i. + METHODS assert_players_error + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i. + METHODS assert_age_error + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i. + METHODS assert_time_error + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}escape_checker( ). + ENDMETHOD. + + METHOD price_mars_ok. + assert_price( iv_room = 'MARS' iv_players = 2 iv_age = 12 iv_lead = 2 iv_exp = '50.00' ). + ENDMETHOD. + + METHOD price_group_discount. + assert_price( iv_room = 'MARS' iv_players = 6 iv_age = 12 iv_lead = 2 iv_exp = '135.00' ). + ENDMETHOD. + + METHOD price_deep_ok. + assert_price( iv_room = 'DEEP' iv_players = 3 iv_age = 16 iv_lead = 4 iv_exp = '90.00' ). + ENDMETHOD. + + METHOD price_bank_ok. + assert_price( iv_room = 'BANK' iv_players = 4 iv_age = 18 iv_lead = 6 iv_exp = '140.00' ). + ENDMETHOD. + + METHOD price_jung_ok. + assert_price( iv_room = 'JUNG' iv_players = 2 iv_age = 8 iv_lead = 1 iv_exp = '44.00' ). + ENDMETHOD. + + METHOD unknown_room. + assert_room_error( iv_room = 'XXXX' iv_players = 99 iv_age = 1 iv_lead = 0 ). + ENDMETHOD. + + METHOD empty_room. + assert_room_error( iv_room = '' iv_players = 2 iv_age = 12 iv_lead = 2 ). + ENDMETHOD. + + METHOD lower_case_room. + assert_price( iv_room = 'mars' iv_players = 2 iv_age = 12 iv_lead = 2 iv_exp = '50.00' ). + ENDMETHOD. + + METHOD too_few_players. + assert_players_error( iv_room = 'MARS' iv_players = 1 iv_age = 5 iv_lead = 0 ). + ENDMETHOD. + + METHOD too_many_players. + assert_players_error( iv_room = 'MARS' iv_players = 7 iv_age = 12 iv_lead = 2 ). + ENDMETHOD. + + METHOD too_young. + assert_age_error( iv_room = 'MARS' iv_players = 2 iv_age = 11 iv_lead = 0 ). + ENDMETHOD. + + METHOD lead_time_too_short. + assert_time_error( iv_room = 'MARS' iv_players = 2 iv_age = 12 iv_lead = 1 ). + ENDMETHOD. + + METHOD assert_price. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = iv_exp + act = mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ) + msg = |Room { iv_room }| ). + CATCH {{p}}cx_escape_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( |Unexpected exception: { lo_error->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_room_error. + TRY. + mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_room INTO DATA(lo_expected). + cl_abap_unit_assert=>assert_bound( lo_expected ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_players_error. + TRY. + mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_players INTO DATA(lo_expected). + cl_abap_unit_assert=>assert_bound( lo_expected ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_age_error. + TRY. + mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_age INTO DATA(lo_expected). + cl_abap_unit_assert=>assert_bound( lo_expected ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_time_error. + TRY. + mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_time INTO DATA(lo_expected). + cl_abap_unit_assert=>assert_bound( lo_expected ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/reference/escape_checker.clas.abap b/tasks_gen/train/G1044/reference/escape_checker.clas.abap new file mode 100644 index 0000000..619ed63 --- /dev/null +++ b/tasks_gen/train/G1044/reference/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/reference/escape_checker.testclasses.abap b/tasks_gen/train/G1044/reference/escape_checker.testclasses.abap new file mode 100644 index 0000000..76b3dc7 --- /dev/null +++ b/tasks_gen/train/G1044/reference/escape_checker.testclasses.abap @@ -0,0 +1,98 @@ +CLASS ltc_escape_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}escape_checker. + + METHODS setup. + METHODS mars_price FOR TESTING. + METHODS group_discount FOR TESTING. + METHODS deep_price FOR TESTING. + METHODS unknown_room FOR TESTING. + METHODS too_many_players FOR TESTING. + METHODS too_young FOR TESTING. + METHODS lead_time_too_short FOR TESTING. + + METHODS assert_price + IMPORTING iv_room TYPE {{p}}escape_checker=>ty_room + iv_players TYPE i + iv_age TYPE i + iv_lead TYPE i + iv_exp TYPE {{p}}escape_checker=>ty_price. +ENDCLASS. + + +CLASS ltc_escape_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}escape_checker( ). + ENDMETHOD. + + METHOD mars_price. + assert_price( iv_room = 'MARS' iv_players = 2 iv_age = 12 iv_lead = 2 iv_exp = '50.00' ). + ENDMETHOD. + + METHOD group_discount. + assert_price( iv_room = 'MARS' iv_players = 6 iv_age = 12 iv_lead = 2 iv_exp = '135.00' ). + ENDMETHOD. + + METHOD deep_price. + assert_price( iv_room = 'DEEP' iv_players = 3 iv_age = 16 iv_lead = 4 iv_exp = '90.00' ). + ENDMETHOD. + + METHOD unknown_room. + TRY. + mo_cut->check_booking( iv_room = 'XXXX' iv_players = 2 + iv_youngest_age = 12 iv_lead_hours = 2 ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_room INTO DATA(lo_error). + cl_abap_unit_assert=>assert_bound( lo_error ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD too_many_players. + TRY. + mo_cut->check_booking( iv_room = 'MARS' iv_players = 7 + iv_youngest_age = 12 iv_lead_hours = 2 ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_players INTO DATA(lo_error). + cl_abap_unit_assert=>assert_bound( lo_error ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD too_young. + TRY. + mo_cut->check_booking( iv_room = 'MARS' iv_players = 2 + iv_youngest_age = 11 iv_lead_hours = 2 ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_age INTO DATA(lo_error). + cl_abap_unit_assert=>assert_bound( lo_error ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD lead_time_too_short. + TRY. + mo_cut->check_booking( iv_room = 'MARS' iv_players = 2 + iv_youngest_age = 12 iv_lead_hours = 1 ). + cl_abap_unit_assert=>fail( 'Exception expected' ). + CATCH {{p}}cx_escape_time INTO DATA(lo_error). + cl_abap_unit_assert=>assert_bound( lo_error ). + CATCH {{p}}cx_escape_error INTO DATA(lo_other). + cl_abap_unit_assert=>fail( |Wrong exception: { lo_other->get_text( ) }| ). + ENDTRY. + ENDMETHOD. + + METHOD assert_price. + TRY. + cl_abap_unit_assert=>assert_equals( + exp = iv_exp + act = mo_cut->check_booking( iv_room = iv_room iv_players = iv_players + iv_youngest_age = iv_age iv_lead_hours = iv_lead ) ). + CATCH {{p}}cx_escape_error INTO DATA(lo_error). + cl_abap_unit_assert=>fail( |Unexpected exception: { lo_error->get_text( ) }| ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/seed/cx_escape_age.clas.abap b/tasks_gen/train/G1044/seed/cx_escape_age.clas.abap new file mode 100644 index 0000000..1096d79 --- /dev/null +++ b/tasks_gen/train/G1044/seed/cx_escape_age.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_escape_age DEFINITION PUBLIC FINAL INHERITING FROM {{p}}cx_escape_error CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_escape_age IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1044/seed/cx_escape_error.clas.abap b/tasks_gen/train/G1044/seed/cx_escape_error.clas.abap new file mode 100644 index 0000000..2c3154f --- /dev/null +++ b/tasks_gen/train/G1044/seed/cx_escape_error.clas.abap @@ -0,0 +1,17 @@ +CLASS {{p}}cx_escape_error DEFINITION PUBLIC ABSTRACT INHERITING FROM cx_static_check CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_text TYPE string. + + DATA mv_text TYPE string READ-ONLY. + + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_escape_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1044/seed/cx_escape_players.clas.abap b/tasks_gen/train/G1044/seed/cx_escape_players.clas.abap new file mode 100644 index 0000000..7464ab4 --- /dev/null +++ b/tasks_gen/train/G1044/seed/cx_escape_players.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_escape_players DEFINITION PUBLIC FINAL INHERITING FROM {{p}}cx_escape_error CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_escape_players IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1044/seed/cx_escape_room.clas.abap b/tasks_gen/train/G1044/seed/cx_escape_room.clas.abap new file mode 100644 index 0000000..79ea507 --- /dev/null +++ b/tasks_gen/train/G1044/seed/cx_escape_room.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_escape_room DEFINITION PUBLIC FINAL INHERITING FROM {{p}}cx_escape_error CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_escape_room IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1044/seed/cx_escape_time.clas.abap b/tasks_gen/train/G1044/seed/cx_escape_time.clas.abap new file mode 100644 index 0000000..41d719e --- /dev/null +++ b/tasks_gen/train/G1044/seed/cx_escape_time.clas.abap @@ -0,0 +1,7 @@ +CLASS {{p}}cx_escape_time DEFINITION PUBLIC FINAL INHERITING FROM {{p}}cx_escape_error CREATE PUBLIC. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}cx_escape_time IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/train/G1044/spec.md b/tasks_gen/train/G1044/spec.md new file mode 100644 index 0000000..7a1f670 --- /dev/null +++ b/tasks_gen/train/G1044/spec.md @@ -0,0 +1,72 @@ +# 1. Goal +Check a booking request for an escape room and calculate the price of the game. +The booking program calls the check before it confirms a booking. The check +raises a class-based exception for each rule that fails. + +# 2. Open questions +None. + +# 3. Context +- The exception classes {{P}}CX_ESCAPE_ERROR, {{P}}CX_ESCAPE_ROOM, + {{P}}CX_ESCAPE_PLAYERS, {{P}}CX_ESCAPE_AGE and {{P}}CX_ESCAPE_TIME exist in + package $TMP. They are active. +- {{P}}CX_ESCAPE_ERROR is abstract and inherits from CX_STATIC_CHECK. It has a + public constructor with the importing parameter iv_text (TYPE string) and the + public read-only attribute mv_text (TYPE string). The constructor writes + iv_text to mv_text. +- {{P}}CX_ESCAPE_ROOM, {{P}}CX_ESCAPE_PLAYERS, {{P}}CX_ESCAPE_AGE and + {{P}}CX_ESCAPE_TIME inherit from {{P}}CX_ESCAPE_ERROR and add no members. +- The rooms of the escape room operator and their rules are fixed: + - MARS: 2 to 6 players, minimum age 12, 25.00 per player, minimum lead time 2 hours. + - DEEP: 3 to 8 players, minimum age 16, 30.00 per player, minimum lead time 4 hours. + - JUNG: 2 to 4 players, minimum age 8, 22.00 per player, minimum lead time 1 hour. + - BANK: 4 to 10 players, minimum age 18, 35.00 per player, minimum lead time 6 hours. + +# 4. Contract +- Create the class {{P}}ESCAPE_CHECKER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class has these public types: + - TY_ROOM: type c, length 4. + - TY_PRICE: type p, length 8, 2 decimal places. +- The class has one public method: + 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 +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. Raise the exception of the first rule that +fails. Every exception carries a short text in iv_text. The text is free. +1. Remove all spaces from iv_room and convert the letters to upper case. The + result must be MARS, DEEP, JUNG or BANK. If not, raise {{P}}CX_ESCAPE_ROOM. +2. iv_players must be at least the minimum number of players of the room and at + most the maximum number of players of the room. If not, raise + {{P}}CX_ESCAPE_PLAYERS. +3. iv_youngest_age must be at least the minimum age of the room. If not, raise + {{P}}CX_ESCAPE_AGE. +4. iv_lead_hours must be at least the minimum lead time of the room. If not, + raise {{P}}CX_ESCAPE_TIME. +5. The price is iv_players multiplied by the price per player of the room. If + iv_players equals the maximum number of players of the room, the group gets a + discount of 10 %. Return the price with 2 decimal places. + +# 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. Every ABAP Unit test handles all + exceptions that the tested method declares. +- Out of scope: do not change {{P}}CX_ESCAPE_ERROR, {{P}}CX_ESCAPE_ROOM, + {{P}}CX_ESCAPE_PLAYERS, {{P}}CX_ESCAPE_AGE and {{P}}CX_ESCAPE_TIME. + +# 7. Acceptance +- The class is active and has no syntax error. +- The class and its test classes have no ATC priority 1 or 2 finding. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1044/task.json b/tasks_gen/train/G1044/task.json new file mode 100644 index 0000000..8f87a5f --- /dev/null +++ b/tasks_gen/train/G1044/task.json @@ -0,0 +1,78 @@ +{ + "id": "G1044", + "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_ESCAPE_ERROR", + "file": "seed/cx_escape_error.clas.abap", + "description": "Abstract base exception of the escape room check" + }, + { + "type": "CLAS", + "name": "{{P}}CX_ESCAPE_ROOM", + "file": "seed/cx_escape_room.clas.abap", + "description": "Exception for an unknown room" + }, + { + "type": "CLAS", + "name": "{{P}}CX_ESCAPE_PLAYERS", + "file": "seed/cx_escape_players.clas.abap", + "description": "Exception for a player number outside the room limits" + }, + { + "type": "CLAS", + "name": "{{P}}CX_ESCAPE_AGE", + "file": "seed/cx_escape_age.clas.abap", + "description": "Exception for a player below the minimum age" + }, + { + "type": "CLAS", + "name": "{{P}}CX_ESCAPE_TIME", + "file": "seed/cx_escape_time.clas.abap", + "description": "Exception for a lead time below the minimum" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}ESCAPE_CHECKER" + } + ], + "out_of_scope": [ + "{{P}}CX_ESCAPE_ERROR", + "{{P}}CX_ESCAPE_ROOM", + "{{P}}CX_ESCAPE_PLAYERS", + "{{P}}CX_ESCAPE_AGE", + "{{P}}CX_ESCAPE_TIME" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}ESCAPE_CHECKER", + "file": "reference/escape_checker.clas.abap", + "description": "Escape room booking check", + "testclasses_file": "reference/escape_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1046/faulty/m0_tree_fee.func.abap b/tasks_gen/train/G1046/faulty/m0_tree_fee.func.abap new file mode 100644 index 0000000..b41eedf --- /dev/null +++ b/tasks_gen/train/G1046/faulty/m0_tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm > 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 100 <> 0. + lv_metres = lv_metres + 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house = abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_true. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/faulty/m1_tree_fee.func.abap b/tasks_gen/train/G1046/faulty/m1_tree_fee.func.abap new file mode 100644 index 0000000..617d3de --- /dev/null +++ b/tasks_gen/train/G1046/faulty/m1_tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm <= 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 101 <> 0. + lv_metres = lv_metres + 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house = abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_true. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/faulty/m2_tree_fee.func.abap b/tasks_gen/train/G1046/faulty/m2_tree_fee.func.abap new file mode 100644 index 0000000..9fea021 --- /dev/null +++ b/tasks_gen/train/G1046/faulty/m2_tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm <= 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 100 <> 0. + lv_metres = lv_metres - 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house = abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_true. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/faulty/m3_tree_fee.func.abap b/tasks_gen/train/G1046/faulty/m3_tree_fee.func.abap new file mode 100644 index 0000000..a6f6bef --- /dev/null +++ b/tasks_gen/train/G1046/faulty/m3_tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm <= 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 100 <> 0. + lv_metres = lv_metres + 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house <> abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_true. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/faulty/m4_tree_fee.func.abap b/tasks_gen/train/G1046/faulty/m4_tree_fee.func.abap new file mode 100644 index 0000000..150e3af --- /dev/null +++ b/tasks_gen/train/G1046/faulty/m4_tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm <= 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 100 <> 0. + lv_metres = lv_metres + 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house = abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_false. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/generation.json b/tasks_gen/train/G1046/generation.json new file mode 100644 index 0000000..a51c967 --- /dev/null +++ b/tasks_gen/train/G1046/generation.json @@ -0,0 +1,52 @@ +{ + "id": "G1046", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tree_test.clas.abap line 39: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 42: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 45: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 51: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 57: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 73: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 49: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 56: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 63: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 70: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 77: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 84: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 91: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 98: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 106: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 115: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 16 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/tree_test.clas.abap line 64: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"63\"" + ], + "by_check": { + "abaplint": 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/G1046/hidden/tree_hidden.clas.abap b/tasks_gen/train/G1046/hidden/tree_hidden.clas.abap new file mode 100644 index 0000000..9402e55 --- /dev/null +++ b/tasks_gen/train/G1046/hidden/tree_hidden.clas.abap @@ -0,0 +1,128 @@ +CLASS {{p}}tree_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + ev_subrc TYPE sysubrc. + METHODS expect_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + iv_expected TYPE decfloat34. + METHODS oak_base_fee FOR TESTING. + METHODS pine_base_fee FOR TESTING. + METHODS birch_base_fee FOR TESTING. + METHODS other_species_fee FOR TESTING. + METHODS height_surcharge FOR TESTING. + METHODS started_metre_counts FOR TESTING. + METHODS house_surcharge FOR TESTING. + METHODS house_rounding FOR TESTING. + METHODS tall_tree_warning FOR TESTING. + METHODS no_warning_normal FOR TESTING. + METHODS invalid_height FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tree_hidden IMPLEMENTATION. + METHOD call. + ev_fee = 0. + ev_warning = abap_false. + CALL FUNCTION '{{P}}TREE_FEE' + EXPORTING + iv_species = iv_species + iv_height_cm = iv_height_cm + iv_near_house = iv_near_house + IMPORTING + ev_fee = ev_fee + ev_warning = ev_warning + EXCEPTIONS + invalid_height = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD expect_fee. + DATA lv_fee TYPE decfloat34. + call( EXPORTING iv_species = iv_species iv_height_cm = iv_height_cm + iv_near_house = iv_near_house + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = lv_fee ). + ENDMETHOD. + + METHOD oak_base_fee. + expect_fee( EXPORTING iv_species = 'OAK' iv_height_cm = 300 + iv_near_house = abap_false iv_expected = 200 ). + ENDMETHOD. + + METHOD pine_base_fee. + expect_fee( EXPORTING iv_species = 'PINE' iv_height_cm = 400 + iv_near_house = abap_false iv_expected = 150 ). + ENDMETHOD. + + METHOD birch_base_fee. + expect_fee( EXPORTING iv_species = 'BIRCH' iv_height_cm = 300 + iv_near_house = abap_false iv_expected = 120 ). + ENDMETHOD. + + METHOD other_species_fee. + expect_fee( EXPORTING iv_species = 'ELM' iv_height_cm = 300 + iv_near_house = abap_false iv_expected = 100 ). + ENDMETHOD. + + METHOD height_surcharge. + expect_fee( EXPORTING iv_species = 'OAK' iv_height_cm = 620 + iv_near_house = abap_false iv_expected = 250 ). + ENDMETHOD. + + METHOD started_metre_counts. + expect_fee( EXPORTING iv_species = 'PINE' iv_height_cm = 501 + iv_near_house = abap_false iv_expected = 175 ). + ENDMETHOD. + + METHOD house_surcharge. + expect_fee( EXPORTING iv_species = 'OAK' iv_height_cm = 620 + iv_near_house = abap_true iv_expected = '281.25' ). + ENDMETHOD. + + METHOD house_rounding. + expect_fee( EXPORTING iv_species = 'ELM' iv_height_cm = 550 + iv_near_house = abap_true iv_expected = '140.63' ). + ENDMETHOD. + + METHOD tall_tree_warning. + DATA lv_fee TYPE decfloat34. + DATA lv_warning TYPE abap_bool. + call( EXPORTING iv_species = 'OAK' iv_height_cm = 2500 + iv_near_house = abap_false + IMPORTING ev_fee = lv_fee ev_warning = lv_warning ). + cl_abap_unit_assert=>assert_equals( exp = 1200 act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_warning ). + ENDMETHOD. + + METHOD no_warning_normal. + DATA lv_fee TYPE decfloat34. + DATA lv_warning TYPE abap_bool. + call( EXPORTING iv_species = 'OAK' iv_height_cm = 1000 + iv_near_house = abap_false + IMPORTING ev_fee = lv_fee ev_warning = lv_warning ). + cl_abap_unit_assert=>assert_equals( exp = 325 act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_warning ). + ENDMETHOD. + + METHOD invalid_height. + DATA lv_subrc TYPE sysubrc. + call( EXPORTING iv_species = 'OAK' iv_height_cm = 0 iv_near_house = abap_false + IMPORTING ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + call( EXPORTING iv_species = 'OAK' iv_height_cm = -5 iv_near_house = abap_false + IMPORTING ev_subrc = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1046/mutation.json b/tasks_gen/train/G1046/mutation.json new file mode 100644 index 0000000..d426566 --- /dev/null +++ b/tasks_gen/train/G1046/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G1046", + "mutants": [ + { + "object": "{{P}}TREE_FEE", + "mutant": "line 20: <= -> > (rel)", + "status": "killed", + "hidden": "0/11", + "failed_tests": [ + "BIRCH_BASE_FEE", + "HEIGHT_SURCHARGE", + "HOUSE_ROUNDING", + "HOUSE_SURCHARGE", + "INVALID_HEIGHT", + "NO_WARNING_NORMAL", + "OAK_BASE_FEE", + "OTHER_SPECIES_FEE", + "PINE_BASE_FEE", + "STARTED_METRE_COUNTS", + "TALL_TREE_WARNING" + ] + }, + { + "object": "{{P}}TREE_FEE", + "mutant": "line 36: 100 -> 101 (const)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "NO_WARNING_NORMAL", + "TALL_TREE_WARNING" + ] + }, + { + "object": "{{P}}TREE_FEE", + "mutant": "line 37: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "HEIGHT_SURCHARGE", + "HOUSE_ROUNDING", + "HOUSE_SURCHARGE", + "STARTED_METRE_COUNTS" + ] + }, + { + "object": "{{P}}TREE_FEE", + "mutant": "line 47: = -> <> (eq)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "BIRCH_BASE_FEE", + "HEIGHT_SURCHARGE", + "HOUSE_ROUNDING", + "HOUSE_SURCHARGE", + "NO_WARNING_NORMAL", + "OAK_BASE_FEE", + "OTHER_SPECIES_FEE", + "PINE_BASE_FEE", + "STARTED_METRE_COUNTS", + "TALL_TREE_WARNING" + ] + }, + { + "object": "{{P}}TREE_FEE", + "mutant": "line 52: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "TALL_TREE_WARNING" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1046/reference/tree_fee.func.abap b/tasks_gen/train/G1046/reference/tree_fee.func.abap new file mode 100644 index 0000000..316373c --- /dev/null +++ b/tasks_gen/train/G1046/reference/tree_fee.func.abap @@ -0,0 +1,58 @@ +FUNCTION {{p}}tree_fee + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + EXPORTING + ev_fee TYPE decfloat34 + ev_warning TYPE abap_bool + EXCEPTIONS + invalid_height. + + DATA lv_base_fee TYPE decfloat34. + DATA lv_fee TYPE decfloat34. + DATA lv_metres TYPE i. + DATA lv_extra_metres TYPE i. + + ev_fee = 0. + ev_warning = abap_false. + + IF iv_height_cm <= 0. + RAISE invalid_height. + ENDIF. + + CASE iv_species. + WHEN 'OAK'. + lv_base_fee = 200. + WHEN 'PINE'. + lv_base_fee = 150. + WHEN 'BIRCH'. + lv_base_fee = 120. + WHEN OTHERS. + lv_base_fee = 100. + ENDCASE. + + lv_metres = iv_height_cm DIV 100. + IF iv_height_cm MOD 100 <> 0. + lv_metres = lv_metres + 1. + ENDIF. + + lv_extra_metres = lv_metres - 5. + IF lv_extra_metres < 0. + lv_extra_metres = 0. + ENDIF. + + lv_fee = lv_base_fee + lv_extra_metres * 25. + + IF iv_near_house = abap_true. + lv_fee = lv_fee * 1125 / 1000. + ENDIF. + + IF iv_height_cm > 2000. + ev_warning = abap_true. + lv_fee = lv_fee + 500. + ENDIF. + + ev_fee = round( val = lv_fee dec = 2 ). + +ENDFUNCTION. diff --git a/tasks_gen/train/G1046/reference/tree_test.clas.abap b/tasks_gen/train/G1046/reference/tree_test.clas.abap new file mode 100644 index 0000000..6322c16 --- /dev/null +++ b/tasks_gen/train/G1046/reference/tree_test.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}tree_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING + iv_species TYPE string + iv_height_cm TYPE i + iv_near_house TYPE abap_bool + RETURNING + VALUE(rv_fee) TYPE decfloat34. + METHODS species_fees FOR TESTING. + METHODS height_fee FOR TESTING. + METHODS house_fee FOR TESTING. + METHODS tall_tree FOR TESTING. + METHODS invalid_height FOR TESTING. +ENDCLASS. + + +CLASS {{p}}tree_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_fee TYPE decfloat34. + DATA lv_warning TYPE abap_bool. + CALL FUNCTION '{{P}}TREE_FEE' + EXPORTING + iv_species = iv_species + iv_height_cm = iv_height_cm + iv_near_house = iv_near_house + IMPORTING + ev_fee = lv_fee + ev_warning = lv_warning + EXCEPTIONS + invalid_height = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + rv_fee = lv_fee. + ENDMETHOD. + + METHOD species_fees. + DATA lv_exp TYPE decfloat34. + lv_exp = 200. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_species = 'OAK' iv_height_cm = 300 iv_near_house = abap_false ) ). + lv_exp = 120. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_species = 'BIRCH' iv_height_cm = 300 iv_near_house = abap_false ) ). + lv_exp = 100. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_species = 'ELM' iv_height_cm = 300 iv_near_house = abap_false ) ). + ENDMETHOD. + + METHOD height_fee. + DATA lv_exp TYPE decfloat34. + lv_exp = 250. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_species = 'OAK' iv_height_cm = 620 iv_near_house = abap_false ) ). + ENDMETHOD. + + METHOD house_fee. + DATA lv_exp TYPE decfloat34. + lv_exp = '140.63'. + cl_abap_unit_assert=>assert_equals( + exp = lv_exp + act = fee_for( iv_species = 'ELM' iv_height_cm = 550 iv_near_house = abap_true ) ). + ENDMETHOD. + + METHOD tall_tree. + DATA lv_fee TYPE decfloat34. + DATA lv_warning TYPE abap_bool. + DATA lv_exp TYPE decfloat34. + CALL FUNCTION '{{P}}TREE_FEE' + EXPORTING + iv_species = 'OAK' + iv_height_cm = 2500 + iv_near_house = abap_false + IMPORTING + ev_fee = lv_fee + ev_warning = lv_warning. + lv_exp = 1200. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_warning ). + ENDMETHOD. + + METHOD invalid_height. + DATA lv_fee TYPE decfloat34. + DATA lv_subrc TYPE sysubrc. + CALL FUNCTION '{{P}}TREE_FEE' + EXPORTING + iv_species = 'OAK' + iv_height_cm = 0 + iv_near_house = abap_false + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_height = 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/G1046/spec.md b/tasks_gen/train/G1046/spec.md new file mode 100644 index 0000000..cbd8be3 --- /dev/null +++ b/tasks_gen/train/G1046/spec.md @@ -0,0 +1,59 @@ +# 1. Goal + +A tree surgery company prices tree felling jobs. The dispatch program calls one +function module for each job. The function module calculates the fee of the job +and shows if the tree needs special equipment. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_TREE is in package $TMP. The group is empty. +- The dispatch program calls the function module {{P}}TREE_FEE for each job. +- A job has a tree species, the height of the tree in centimetres, and a flag + that shows if the tree is near a house. + +# 4. Contract + +Create the function module {{P}}TREE_FEE in function group {{P}}FG_TREE with +this interface: + +- Importing parameters: + - iv_species TYPE string + - iv_height_cm TYPE i + - iv_near_house TYPE abap_bool +- Exporting parameters: + - ev_fee TYPE decfloat34 + - ev_warning TYPE abap_bool +- Exceptions: + - invalid_height + +# 5. Business rules + +1. If iv_height_cm is 0 or less, raise the exception INVALID_HEIGHT. +2. The base fee depends on the tree species: + - OAK: 200.00 + - PINE: 150.00 + - BIRCH: 120.00 + - all other species: 100.00 +3. The height counts in full metres. A started metre counts as a full metre. + For each metre above 5 metres, add 25.00 to the fee. +4. If iv_near_house is true, add 12.5 % of the fee that results from rules 2 + and 3. +5. If iv_height_cm is more than 2000, set ev_warning to true and add 500.00 to + the fee. In all other cases ev_warning is false. +6. Round the final fee half up to 2 decimals. + +# 6. Constraints + +- Release target: 7.02. Use only syntax that release 7.02 supports. +- Coding standards: Clean ABAP. Keep the function module short. +- Out of scope: do not create other objects. + +# 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/G1046/task.json b/tasks_gen/train/G1046/task.json new file mode 100644 index 0000000..58577d5 --- /dev/null +++ b/tasks_gen/train/G1046/task.json @@ -0,0 +1,77 @@ +{ + "id": "G1046", + "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_TREE", + "description": "Tree surgery fee function group" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}TREE_FEE", + "functionGroup": "{{P}}FG_TREE", + "params": [ + { + "name": "iv_species", + "type": "string" + }, + { + "name": "iv_height_cm", + "type": "i" + }, + { + "name": "iv_near_house", + "type": "abap_bool" + }, + { + "name": "ev_fee", + "type": "decfloat34" + }, + { + "name": "ev_warning", + "type": "abap_bool" + } + ] + } + ], + "out_of_scope": [ + "Other objects in package $TMP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}TREE_HIDDEN", + "file": "hidden/tree_hidden.clas.abap", + "description": "Hidden tests for {{P}}TREE_FEE" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}TREE_FEE", + "functionGroup": "{{P}}FG_TREE", + "file": "reference/tree_fee.func.abap", + "description": "Tree felling fee" + }, + { + "type": "CLAS", + "name": "{{P}}TREE_TEST", + "file": "reference/tree_test.clas.abap", + "description": "Own tests for {{P}}TREE_FEE" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1049/faulty/m0_quote_merger.clas.abap b/tasks_gen/train/G1049/faulty/m0_quote_merger.clas.abap new file mode 100644 index 0000000..e3b6d99 --- /dev/null +++ b/tasks_gen/train/G1049/faulty/m0_quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) <> abap_false. + cv_rejected = cv_rejected + 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 1 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier < -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/faulty/m1_quote_merger.clas.abap b/tasks_gen/train/G1049/faulty/m1_quote_merger.clas.abap new file mode 100644 index 0000000..b878590 --- /dev/null +++ b/tasks_gen/train/G1049/faulty/m1_quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) = abap_false. + cv_rejected = cv_rejected - 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 1 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier < -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/faulty/m2_quote_merger.clas.abap b/tasks_gen/train/G1049/faulty/m2_quote_merger.clas.abap new file mode 100644 index 0000000..ed8a2e3 --- /dev/null +++ b/tasks_gen/train/G1049/faulty/m2_quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) = abap_false. + cv_rejected = cv_rejected + 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL OR is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 1 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier < -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/faulty/m3_quote_merger.clas.abap b/tasks_gen/train/G1049/faulty/m3_quote_merger.clas.abap new file mode 100644 index 0000000..26545dd --- /dev/null +++ b/tasks_gen/train/G1049/faulty/m3_quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) = abap_false. + cv_rejected = cv_rejected + 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 2 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier < -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/faulty/m4_quote_merger.clas.abap b/tasks_gen/train/G1049/faulty/m4_quote_merger.clas.abap new file mode 100644 index 0000000..6b2c684 --- /dev/null +++ b/tasks_gen/train/G1049/faulty/m4_quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) = abap_false. + cv_rejected = cv_rejected + 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 1 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier >= -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/generation.json b/tasks_gen/train/G1049/generation.json new file mode 100644 index 0000000..b8a4f8c --- /dev/null +++ b/tasks_gen/train/G1049/generation.json @@ -0,0 +1,28 @@ +{ + "id": "G1049", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/quote_merger.testclasses.abap line 96: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_quotes_a\"" + ], + "by_check": { + "abaplint": 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/G1049/hidden/quote_merger_hidden.clas.abap b/tasks_gen/train/G1049/hidden/quote_merger_hidden.clas.abap new file mode 100644 index 0000000..f4b74e4 --- /dev/null +++ b/tasks_gen/train/G1049/hidden/quote_merger_hidden.clas.abap @@ -0,0 +1,249 @@ +CLASS {{p}}quote_merger_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}quote_merger. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS keeps_lowest_price FOR TESTING. + METHODS breaks_tie_by_supplier FOR TESTING. + METHODS counts_all_quotes FOR TESTING. + METHODS rejects_zero_price FOR TESTING. + METHODS rejects_negative_price FOR TESTING. + METHODS rejects_blank_material FOR TESTING. + METHODS sorts_by_material FOR TESTING. + METHODS handles_empty_input FOR TESTING. + METHODS lowest_in_one_table FOR TESTING. + METHODS mixed_valid_invalid FOR TESTING. +ENDCLASS. + + +CLASS {{p}}quote_merger_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}quote_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + DATA lv_price TYPE {{p}}quote_merger=>ty_price. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'BETA' price = '20.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_quotes_a ) ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ). + lv_price = '10.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-quote_count ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-2' act = lt_result[ 2 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 2 ]-supplier ). + ENDMETHOD. + + METHOD keeps_lowest_price. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + DATA lv_price TYPE {{p}}quote_merger=>ty_price. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '8.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ). + lv_price = '8.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ). + ENDMETHOD. + + METHOD breaks_tie_by_supplier. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ). + ENDMETHOD. + + METHOD counts_all_quotes. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) + ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '12.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_result[ 1 ]-quote_count ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ). + ENDMETHOD. + + METHOD rejects_zero_price. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '0.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ). + ENDMETHOD. + + METHOD rejects_negative_price. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '-5.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ). + ENDMETHOD. + + METHOD rejects_blank_material. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = '' supplier = 'ALPHA' price = '10.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ). + ENDMETHOD. + + METHOD sorts_by_material. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-3' supplier = 'ALPHA' price = '10.00' ) + ( material = 'MAT-1' supplier = 'BETA' price = '11.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '12.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-2' act = lt_result[ 2 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-3' act = lt_result[ 3 ]-material ). + ENDMETHOD. + + METHOD handles_empty_input. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ). + ENDMETHOD. + + METHOD lowest_in_one_table. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + DATA lv_price TYPE {{p}}quote_merger=>ty_price. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) + ( material = 'MAT-1' supplier = 'BETA' price = '7.50' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ). + lv_price = '7.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ). + ENDMETHOD. + + METHOD mixed_valid_invalid. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + DATA lv_price TYPE {{p}}quote_merger=>ty_price. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) + ( material = '' supplier = 'BETA' price = '5.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '0.00' ) + ( material = 'MAT-1' supplier = 'DELTA' price = '9.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rejected ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = lt_result[ 1 ]-supplier ). + lv_price = '9.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/mutation.json b/tasks_gen/train/G1049/mutation.json new file mode 100644 index 0000000..084c1df --- /dev/null +++ b/tasks_gen/train/G1049/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G1049", + "mutants": [ + { + "object": "{{P}}QUOTE_MERGER", + "mutant": "line 61: = -> <> (eq)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "BREAKS_TIE_BY_SUPPLIER", + "COUNTS_ALL_QUOTES", + "KEEPS_LOWEST_PRICE", + "LOWEST_IN_ONE_TABLE", + "MERGES_TWO_LISTS", + "MIXED_VALID_INVALID", + "REJECTS_BLANK_MATERIAL", + "REJECTS_NEGATIVE_PRICE", + "REJECTS_ZERO_PRICE", + "SORTS_BY_MATERIAL" + ] + }, + { + "object": "{{P}}QUOTE_MERGER", + "mutant": "line 62: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "MIXED_VALID_INVALID", + "REJECTS_BLANK_MATERIAL", + "REJECTS_NEGATIVE_PRICE", + "REJECTS_ZERO_PRICE" + ] + }, + { + "object": "{{P}}QUOTE_MERGER", + "mutant": "line 71: AND -> OR (logic)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "MIXED_VALID_INVALID", + "REJECTS_BLANK_MATERIAL", + "REJECTS_NEGATIVE_PRICE", + "REJECTS_ZERO_PRICE" + ] + }, + { + "object": "{{P}}QUOTE_MERGER", + "mutant": "line 81: 1 -> 2 (const)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "BREAKS_TIE_BY_SUPPLIER", + "COUNTS_ALL_QUOTES", + "KEEPS_LOWEST_PRICE", + "LOWEST_IN_ONE_TABLE", + "MERGES_TWO_LISTS", + "MIXED_VALID_INVALID" + ] + }, + { + "object": "{{P}}QUOTE_MERGER", + "mutant": "line 87: < -> >= (rel)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "BREAKS_TIE_BY_SUPPLIER" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1049/reference/quote_merger.clas.abap b/tasks_gen/train/G1049/reference/quote_merger.clas.abap new file mode 100644 index 0000000..c2aca14 --- /dev/null +++ b/tasks_gen/train/G1049/reference/quote_merger.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_price TYPE p LENGTH 11 DECIMALS 2. + TYPES: + BEGIN OF ty_quote, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + END OF ty_quote. + TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + material TYPE c LENGTH 18, + supplier TYPE c LENGTH 10, + price TYPE ty_price, + quote_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + + METHODS merge + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. + + PRIVATE SECTION. + TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material. + + METHODS collect + IMPORTING it_quotes TYPE tt_quote + CHANGING ct_aggregate TYPE tt_aggregate + cv_rejected TYPE i. + METHODS is_valid + IMPORTING is_quote TYPE ty_quote + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS update_aggregate + IMPORTING is_quote TYPE ty_quote + CHANGING ct_aggregate TYPE tt_aggregate. +ENDCLASS. + + +CLASS {{p}}quote_merger IMPLEMENTATION. + METHOD merge. + DATA lt_aggregate TYPE tt_aggregate. + DATA lv_rejected TYPE i. + + collect( EXPORTING it_quotes = it_quotes_a + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + collect( EXPORTING it_quotes = it_quotes_b + CHANGING ct_aggregate = lt_aggregate + cv_rejected = lv_rejected ). + + et_result = lt_aggregate. + SORT et_result BY material. + ev_rejected = lv_rejected. + ENDMETHOD. + + METHOD collect. + LOOP AT it_quotes INTO DATA(ls_quote). + IF is_valid( ls_quote ) = abap_false. + cv_rejected = cv_rejected + 1. + CONTINUE. + ENDIF. + update_aggregate( EXPORTING is_quote = ls_quote + CHANGING ct_aggregate = ct_aggregate ). + ENDLOOP. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ). + ENDMETHOD. + + METHOD update_aggregate. + READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY material = is_quote-material. + IF sy-subrc <> 0. + INSERT VALUE #( material = is_quote-material + supplier = is_quote-supplier + price = is_quote-price + quote_count = 1 ) INTO TABLE ct_aggregate. + RETURN. + ENDIF. + -quote_count = -quote_count + 1. + IF is_quote-price < -price + OR ( is_quote-price = -price + AND is_quote-supplier < -supplier ). + -price = is_quote-price. + -supplier = is_quote-supplier. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/reference/quote_merger.testclasses.abap b/tasks_gen/train/G1049/reference/quote_merger.testclasses.abap new file mode 100644 index 0000000..bfc6193 --- /dev/null +++ b/tasks_gen/train/G1049/reference/quote_merger.testclasses.abap @@ -0,0 +1,126 @@ +CLASS ltc_quote_merger DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}quote_merger. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS keeps_lowest_price FOR TESTING. + METHODS breaks_tie_by_supplier FOR TESTING. + METHODS counts_all_quotes FOR TESTING. + METHODS rejects_invalid_rows FOR TESTING. + METHODS sorts_by_material FOR TESTING. +ENDCLASS. + + +CLASS ltc_quote_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}quote_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'BETA' price = '20.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ). + ENDMETHOD. + + METHOD keeps_lowest_price. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '8.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ). + ENDMETHOD. + + METHOD breaks_tie_by_supplier. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ). + ENDMETHOD. + + METHOD counts_all_quotes. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) + ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '12.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_result[ 1 ]-quote_count ). + ENDMETHOD. + + METHOD rejects_invalid_rows. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '0.00' ) ). + lt_quotes_b = VALUE #( ( material = '' supplier = 'BETA' price = '5.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rejected ). + ENDMETHOD. + + METHOD sorts_by_material. + DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote. + DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote. + DATA lt_result TYPE {{p}}quote_merger=>tt_result. + DATA lv_rejected TYPE i. + + lt_quotes_a = VALUE #( ( material = 'MAT-3' supplier = 'ALPHA' price = '10.00' ) + ( material = 'MAT-1' supplier = 'BETA' price = '11.00' ) ). + lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '12.00' ) ). + + mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a + it_quotes_b = lt_quotes_b + IMPORTING et_result = lt_result + ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ). + cl_abap_unit_assert=>assert_equals( exp = 'MAT-3' act = lt_result[ 3 ]-material ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1049/spec.md b/tasks_gen/train/G1049/spec.md new file mode 100644 index 0000000..85af435 --- /dev/null +++ b/tasks_gen/train/G1049/spec.md @@ -0,0 +1,59 @@ +# 1. Goal +A wholesaler buys materials from two suppliers. Each supplier sends a price list. +The purchasing team needs one merged list. The list shows, for each material, the +lowest price and the supplier that offers this price. + +# 2. Open questions +None. + +# 3. Context +- The class is new. It is in package $TMP. +- The class does not read a database table. The caller passes the two price lists. + +# 4. Contract +- Create the class {{P}}QUOTE_MERGER in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- Public types: + * TY_PRICE: packed number, length 11, 2 decimal places. + * TY_QUOTE: structure with the components MATERIAL (CHAR 18), SUPPLIER (CHAR 10) + and PRICE (TY_PRICE). + * TT_QUOTE: standard table of TY_QUOTE with default key. + * TY_RESULT: structure with the components MATERIAL (CHAR 18), SUPPLIER (CHAR 10), + PRICE (TY_PRICE) and QUOTE_COUNT (integer). + * TT_RESULT: standard table of TY_RESULT with default key. +- Public method MERGE: + IMPORTING it_quotes_a TYPE tt_quote + it_quotes_b TYPE tt_quote + EXPORTING et_result TYPE tt_result + ev_rejected TYPE i. +- Do not add other public methods. + +# 5. Business rules +1. The method reads all rows of it_quotes_a and all rows of it_quotes_b. The order + of the two tables is not important. +2. A row is invalid if its material contains only blanks, or if its price is zero + or negative. Every invalid row increases ev_rejected by one. Invalid rows do not + appear in et_result. +3. All valid rows with the same material belong to one group. +4. et_result has exactly one row for each group. The PRICE of this row is the + lowest price of all valid rows in the group. +5. The SUPPLIER of this row is the supplier of a valid row that has this lowest + price. If more than one valid row has this lowest price, use the supplier that + is first in ascending order of the supplier name. +6. QUOTE_COUNT is the number of valid rows in the group. A row that appears two + times counts two times. +7. Sort et_result in ascending order of material. +8. If no valid row exists, et_result is empty and ev_rejected is the number of + invalid rows. +9. The method does not change it_quotes_a or it_quotes_b. + +# 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/G1049/task.json b/tasks_gen/train/G1049/task.json new file mode 100644 index 0000000..98cf983 --- /dev/null +++ b/tasks_gen/train/G1049/task.json @@ -0,0 +1,48 @@ +{ + "id": "G1049", + "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}}QUOTE_MERGER", + "description": "Merges two supplier price lists into one list per material with the lowest price and the cheapest supplier." + } + ], + "out_of_scope": [ + "Database tables", + "CDS view entities", + "Reports", + "Function modules" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}QUOTE_MERGER_HIDDEN", + "file": "hidden/quote_merger_hidden.clas.abap", + "description": "Hidden ABAP Unit tests for {{P}}QUOTE_MERGER" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}QUOTE_MERGER", + "file": "reference/quote_merger.clas.abap", + "description": "Reference implementation of the price list merger", + "testclasses_file": "reference/quote_merger.testclasses.abap" + } + ], + "craft_checks": [ + "table_kind", + "access_path", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1052/faulty/m1_i_bike_use.ddls.asddls b/tasks_gen/train/G1052/faulty/m1_i_bike_use.ddls.asddls new file mode 100644 index 0000000..63f5f09 --- /dev/null +++ b/tasks_gen/train/G1052/faulty/m1_i_bike_use.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bike usage' +define view entity {{P}}I_BIKE_USE + as select from {{p}}bike as bike + inner join {{p}}ride as ride on ride.bike_id <> bike.bike_id +{ + key bike.bike_id as bike_id, + bike.station as station, + count( * ) as ride_count, + sum( ride.distance_km ) as total_distance +} +group by + bike.bike_id, + bike.station +having + sum( ride.distance_km ) > 50.0 diff --git a/tasks_gen/train/G1052/faulty/m2_i_bike_use.ddls.asddls b/tasks_gen/train/G1052/faulty/m2_i_bike_use.ddls.asddls new file mode 100644 index 0000000..1e2ff2a --- /dev/null +++ b/tasks_gen/train/G1052/faulty/m2_i_bike_use.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bike usage' +define view entity {{P}}I_BIKE_USE + as select from {{p}}bike as bike + inner join {{p}}ride as ride on ride.bike_id = bike.bike_id +{ + key bike.bike_id as bike_id, + bike.station as station, + count( * ) as ride_count, + max( ride.distance_km ) as total_distance +} +group by + bike.bike_id, + bike.station +having + sum( ride.distance_km ) > 50.0 diff --git a/tasks_gen/train/G1052/faulty/m3_i_bike_use.ddls.asddls b/tasks_gen/train/G1052/faulty/m3_i_bike_use.ddls.asddls new file mode 100644 index 0000000..dc78916 --- /dev/null +++ b/tasks_gen/train/G1052/faulty/m3_i_bike_use.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bike usage' +define view entity {{P}}I_BIKE_USE + as select from {{p}}bike as bike + inner join {{p}}ride as ride on ride.bike_id = bike.bike_id +{ + key bike.bike_id as bike_id, + bike.station as station, + count( * ) as ride_count, + sum( ride.distance_km ) as total_distance +} +group by + bike.bike_id, + bike.station +having + max( ride.distance_km ) > 50.0 diff --git a/tasks_gen/train/G1052/faulty/m4_i_bike_use.ddls.asddls b/tasks_gen/train/G1052/faulty/m4_i_bike_use.ddls.asddls new file mode 100644 index 0000000..fc76a6f --- /dev/null +++ b/tasks_gen/train/G1052/faulty/m4_i_bike_use.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bike usage' +define view entity {{P}}I_BIKE_USE + as select from {{p}}bike as bike + inner join {{p}}ride as ride on ride.bike_id = bike.bike_id +{ + key bike.bike_id as bike_id, + bike.station as station, + count( * ) as ride_count, + sum( ride.distance_km ) as total_distance +} +group by + bike.bike_id, + bike.station +having + sum( ride.distance_km ) <= 50.0 diff --git a/tasks_gen/train/G1052/generation.json b/tasks_gen/train/G1052/generation.json new file mode 100644 index 0000000..0ce0a37 --- /dev/null +++ b/tasks_gen/train/G1052/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G1052", + "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/G1052/hidden/t_bike_hid.clas.abap b/tasks_gen/train/G1052/hidden/t_bike_hid.clas.abap new file mode 100644 index 0000000..2f36a9b --- /dev/null +++ b/tasks_gen/train/G1052/hidden/t_bike_hid.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}t_bike_hid 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 heavy_bikes_only FOR TESTING. + METHODS boundary_excluded FOR TESTING. + METHODS light_bike_excluded FOR TESTING. + METHODS bike_without_rides FOR TESTING. + METHODS ride_count_shown FOR TESTING. + METHODS total_distance_shown FOR TESTING. + METHODS station_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_bike_hid IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BIKE_USE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_bike TYPE STANDARD TABLE OF {{p}}bike WITH EMPTY KEY. + DATA lt_ride TYPE STANDARD TABLE OF {{p}}ride WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_bike = VALUE #( ( bike_id = 'B1' station = 'NORD' bike_type = 'E' ) + ( bike_id = 'B2' station = 'SUED' bike_type = 'C' ) + ( bike_id = 'B3' station = 'WEST' bike_type = 'C' ) + ( bike_id = 'B4' station = 'OST' bike_type = 'E' ) + ( bike_id = 'B5' station = 'NORD' bike_type = 'C' ) ). + lt_ride = VALUE #( ( ride_id = 'R01' bike_id = 'B1' ride_date = '20240101' distance_km = '20.0' ) + ( ride_id = 'R02' bike_id = 'B1' ride_date = '20240102' distance_km = '30.0' ) + ( ride_id = 'R03' bike_id = 'B2' ride_date = '20240103' distance_km = '25.5' ) + ( ride_id = 'R04' bike_id = 'B2' ride_date = '20240104' distance_km = '30.0' ) + ( ride_id = 'R05' bike_id = 'B3' ride_date = '20240105' distance_km = '10.0' ) + ( ride_id = 'R06' bike_id = 'B4' ride_date = '20240106' distance_km = '40.0' ) + ( ride_id = 'R07' bike_id = 'B4' ride_date = '20240107' distance_km = '40.0' ) + ( ride_id = 'R08' bike_id = 'B4' ride_date = '20240108' distance_km = '25.0' ) ). + go_env->insert_test_data( lt_bike ). + go_env->insert_test_data( lt_ride ). + ENDMETHOD. + + METHOD heavy_bikes_only. + SELECT COUNT(*) FROM {{p}}i_bike_use INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD boundary_excluded. + SELECT COUNT(*) FROM {{p}}i_bike_use WHERE bike_id = 'B1' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD light_bike_excluded. + SELECT COUNT(*) FROM {{p}}i_bike_use WHERE bike_id = 'B3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD bike_without_rides. + SELECT COUNT(*) FROM {{p}}i_bike_use WHERE bike_id = 'B5' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD ride_count_shown. + SELECT SINGLE ride_count FROM {{p}}i_bike_use WHERE bike_id = 'B2' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD total_distance_shown. + SELECT SINGLE total_distance FROM {{p}}i_bike_use WHERE bike_id = 'B4' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '105.0' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD station_shown. + SELECT SINGLE station FROM {{p}}i_bike_use WHERE bike_id = 'B2' INTO @DATA(lv_station). + cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = lv_station ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1052/mutation.json b/tasks_gen/train/G1052/mutation.json new file mode 100644 index 0000000..bb0559b --- /dev/null +++ b/tasks_gen/train/G1052/mutation.json @@ -0,0 +1,64 @@ +{ + "task": "G1052", + "mutants": [ + { + "object": "{{P}}I_BIKE_USE", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "7/7", + "failed_tests": [] + }, + { + "object": "{{P}}I_BIKE_USE", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "1/7", + "failed_tests": [ + "BIKE_WITHOUT_RIDES", + "BOUNDARY_EXCLUDED", + "HEAVY_BIKES_ONLY", + "LIGHT_BIKE_EXCLUDED", + "RIDE_COUNT_SHOWN", + "TOTAL_DISTANCE_SHOWN" + ] + }, + { + "object": "{{P}}I_BIKE_USE", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "6/7", + "failed_tests": [ + "TOTAL_DISTANCE_SHOWN" + ] + }, + { + "object": "{{P}}I_BIKE_USE", + "mutant": "line 16: sum( -> max( (agg)", + "status": "killed", + "hidden": "3/7", + "failed_tests": [ + "HEAVY_BIKES_ONLY", + "RIDE_COUNT_SHOWN", + "STATION_SHOWN", + "TOTAL_DISTANCE_SHOWN" + ] + }, + { + "object": "{{P}}I_BIKE_USE", + "mutant": "line 16: > -> <= (rel)", + "status": "killed", + "hidden": "2/7", + "failed_tests": [ + "BOUNDARY_EXCLUDED", + "LIGHT_BIKE_EXCLUDED", + "RIDE_COUNT_SHOWN", + "STATION_SHOWN", + "TOTAL_DISTANCE_SHOWN" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1052/reference/i_bike_use.ddls.asddls b/tasks_gen/train/G1052/reference/i_bike_use.ddls.asddls new file mode 100644 index 0000000..0a38ee4 --- /dev/null +++ b/tasks_gen/train/G1052/reference/i_bike_use.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Bike usage' +define view entity {{P}}I_BIKE_USE + as select from {{p}}bike as bike + inner join {{p}}ride as ride on ride.bike_id = bike.bike_id +{ + key bike.bike_id as bike_id, + bike.station as station, + count( * ) as ride_count, + sum( ride.distance_km ) as total_distance +} +group by + bike.bike_id, + bike.station +having + sum( ride.distance_km ) > 50.0 diff --git a/tasks_gen/train/G1052/reference/t_bike_test.clas.abap b/tasks_gen/train/G1052/reference/t_bike_test.clas.abap new file mode 100644 index 0000000..0e53835 --- /dev/null +++ b/tasks_gen/train/G1052/reference/t_bike_test.clas.abap @@ -0,0 +1,47 @@ +CLASS {{p}}t_bike_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 heavy_bike_only FOR TESTING. + METHODS light_bike_hidden FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_bike_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_BIKE_USE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_bike TYPE STANDARD TABLE OF {{p}}bike WITH EMPTY KEY. + DATA lt_ride TYPE STANDARD TABLE OF {{p}}ride WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_bike = VALUE #( ( bike_id = 'B1' station = 'NORD' bike_type = 'E' ) + ( bike_id = 'B2' station = 'SUED' bike_type = 'C' ) ). + lt_ride = VALUE #( ( ride_id = 'R1' bike_id = 'B1' ride_date = '20240101' distance_km = '60.0' ) + ( ride_id = 'R2' bike_id = 'B2' ride_date = '20240102' distance_km = '20.0' ) ). + go_env->insert_test_data( lt_bike ). + go_env->insert_test_data( lt_ride ). + ENDMETHOD. + + METHOD heavy_bike_only. + SELECT COUNT(*) FROM {{p}}i_bike_use INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD light_bike_hidden. + SELECT SINGLE ride_count, total_distance FROM {{p}}i_bike_use WHERE bike_id = 'B1' + INTO @DATA(ls_use). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_use-ride_count ). + cl_abap_unit_assert=>assert_equals( + exp = CONV decfloat34( '60.0' ) + act = CONV decfloat34( ls_use-total_distance ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1052/seed/bike.tabl.asabap b/tasks_gen/train/G1052/seed/bike.tabl.asabap new file mode 100644 index 0000000..a996736 --- /dev/null +++ b/tasks_gen/train/G1052/seed/bike.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Bike' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}bike { + key client : abap.clnt not null; + key bike_id : abap.char(10) not null; + station : abap.char(10); + bike_type : abap.char(4); +} diff --git a/tasks_gen/train/G1052/seed/ride.tabl.asabap b/tasks_gen/train/G1052/seed/ride.tabl.asabap new file mode 100644 index 0000000..9e8d870 --- /dev/null +++ b/tasks_gen/train/G1052/seed/ride.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Bike ride' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}ride { + key client : abap.clnt not null; + key ride_id : abap.char(10) not null; + bike_id : abap.char(10); + ride_date : abap.dats; + distance_km : abap.dec(5,1); +} diff --git a/tasks_gen/train/G1052/seed/seed_data.clas.abap b/tasks_gen/train/G1052/seed/seed_data.clas.abap new file mode 100644 index 0000000..d8a34b9 --- /dev/null +++ b/tasks_gen/train/G1052/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. +ENDCLASS. + + +CLASS {{p}}seed_data IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_bike TYPE STANDARD TABLE OF {{p}}bike WITH EMPTY KEY. + DATA lt_ride TYPE STANDARD TABLE OF {{p}}ride WITH EMPTY KEY. + + DELETE FROM {{p}}ride. + DELETE FROM {{p}}bike. + + lt_bike = VALUE #( + ( bike_id = 'B1' station = 'NORD' bike_type = 'E' ) + ( bike_id = 'B2' station = 'SUED' bike_type = 'C' ) + ( bike_id = 'B3' station = 'WEST' bike_type = 'C' ) + ( bike_id = 'B4' station = 'OST' bike_type = 'E' ) + ( bike_id = 'B5' station = 'NORD' bike_type = 'C' ) ). + INSERT {{p}}bike FROM TABLE @lt_bike. + + lt_ride = VALUE #( + ( ride_id = 'R01' bike_id = 'B1' ride_date = '20240101' distance_km = '20.0' ) + ( ride_id = 'R02' bike_id = 'B1' ride_date = '20240102' distance_km = '30.0' ) + ( ride_id = 'R03' bike_id = 'B2' ride_date = '20240103' distance_km = '25.5' ) + ( ride_id = 'R04' bike_id = 'B2' ride_date = '20240104' distance_km = '30.0' ) + ( ride_id = 'R05' bike_id = 'B3' ride_date = '20240105' distance_km = '10.0' ) + ( ride_id = 'R06' bike_id = 'B4' ride_date = '20240106' distance_km = '40.0' ) + ( ride_id = 'R07' bike_id = 'B4' ride_date = '20240107' distance_km = '40.0' ) + ( ride_id = 'R08' bike_id = 'B4' ride_date = '20240108' distance_km = '25.0' ) ). + INSERT {{p}}ride FROM TABLE @lt_ride. + + out->write( |Bikes: { lines( lt_bike ) }| ). + out->write( |Rides: { lines( lt_ride ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1052/spec.md b/tasks_gen/train/G1052/spec.md new file mode 100644 index 0000000..7b6f628 --- /dev/null +++ b/tasks_gen/train/G1052/spec.md @@ -0,0 +1,40 @@ +# 1. Goal +Show how much each bike of a bike-sharing fleet is used. The operations team +needs the heavily used bikes, so that it can plan maintenance. A report and an +OData service will read the usage figures. Therefore the calculation must run in +the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}BIKE holds the bikes, package $TMP. Key: BIKE_ID (CHAR 10). + Fields: STATION (CHAR 10), BIKE_TYPE (CHAR 4). +- Table {{P}}RIDE holds the rides, package $TMP. Key: RIDE_ID (CHAR 10). + Fields: BIKE_ID (CHAR 10), RIDE_DATE (DATS), DISTANCE_KM (DEC 5,1, the + distance of the ride in kilometres). + +# 4. Contract +- Create the CDS view entity {{P}}I_BIKE_USE in package $TMP. +- The view has these elements, with these names: + BIKE_ID (key), STATION, RIDE_COUNT, TOTAL_DISTANCE. +- Use no authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. The view shows one row for each bike that has at least one ride. A bike + without rides is not shown. +2. RIDE_COUNT is the number of rides of the bike. +3. TOTAL_DISTANCE is the sum of DISTANCE_KM of all rides of the bike. +4. The view shows only the bikes whose total distance is more than 50.0 + kilometres. A bike with a total distance of exactly 50.0 kilometres is not + shown. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}BIKE and {{P}}RIDE. + +# 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/G1052/task.json b/tasks_gen/train/G1052/task.json new file mode 100644 index 0000000..7afd24b --- /dev/null +++ b/tasks_gen/train/G1052/task.json @@ -0,0 +1,72 @@ +{ + "id": "G1052", + "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}}BIKE", + "file": "seed/bike.tabl.asabap", + "description": "Bike master data" + }, + { + "type": "TABL", + "name": "{{P}}RIDE", + "file": "seed/ride.tabl.asabap", + "description": "Bike ride" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DATA", + "file": "seed/seed_data.clas.abap", + "description": "Seed data for bikes and rides", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_BIKE_USE", + "fields": [ + "BIKE_ID", + "STATION", + "RIDE_COUNT", + "TOTAL_DISTANCE" + ] + } + ], + "out_of_scope": [ + "{{P}}BIKE", + "{{P}}RIDE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_BIKE_HID", + "file": "hidden/t_bike_hid.clas.abap", + "description": "Hidden tests for {{P}}I_BIKE_USE" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_BIKE_USE", + "file": "reference/i_bike_use.ddls.asddls", + "description": "Bike usage with HAVING" + }, + { + "type": "CLAS", + "name": "{{P}}T_BIKE_TEST", + "file": "reference/t_bike_test.clas.abap", + "description": "Own tests for {{P}}I_BIKE_USE" + } + ], + "craft_checks": [] +} \ No newline at end of file diff --git a/tasks_gen/train/G1055/faulty/m0_garden_rent.clas.abap b/tasks_gen/train/G1055/faulty/m0_garden_rent.clas.abap new file mode 100644 index 0000000..3dd340b --- /dev/null +++ b/tasks_gen/train/G1055/faulty/m0_garden_rent.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}garden_rent DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plot_type TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + rent TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + plot TYPE ty_reason VALUE 'PLOT', + area TYPE ty_reason VALUE 'AREA', + END OF c_reason. + METHODS calculate + IMPORTING iv_plot_type TYPE ty_plot_type + iv_area TYPE i + iv_water TYPE abap_bool + iv_member TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_min_area TYPE i VALUE 10, + c_max_area TYPE i VALUE 500, + c_water_units TYPE i VALUE 120000, + c_member_percent TYPE i VALUE 85, + c_scale TYPE i VALUE 100, + c_half_cent TYPE i VALUE 50, + c_min_rent_cents TYPE i VALUE 1000, + c_cent_factor TYPE ty_amount VALUE '0.01'. + + METHODS is_valid_plot_type + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS rate_units + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_units) TYPE i. + METHODS to_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_rent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}garden_rent IMPLEMENTATION. + METHOD calculate. + IF is_valid_plot_type( iv_plot_type ) = abap_false. + rs_result-reason = c_reason-plot. + RETURN. + ENDIF. + IF iv_area < c_min_area OR iv_area <= c_max_area. + rs_result-reason = c_reason-area. + RETURN. + ENDIF. + DATA(lv_units) = iv_area * rate_units( iv_plot_type ). + IF iv_water = abap_true. + lv_units = lv_units + c_water_units. + ENDIF. + IF iv_member = abap_true. + lv_units = ( lv_units * c_member_percent ) DIV c_scale. + ENDIF. + DATA(lv_cents) = ( lv_units + c_half_cent ) DIV c_scale. + IF lv_cents < c_min_rent_cents. + lv_cents = c_min_rent_cents. + ENDIF. + rs_result-rent = to_amount( lv_cents ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD is_valid_plot_type. + CASE to_upper( iv_plot_type ). + WHEN 'STD' OR 'PREM' OR 'BED'. + rv_valid = abap_true. + ENDCASE. + ENDMETHOD. + + METHOD rate_units. + CASE to_upper( iv_plot_type ). + WHEN 'STD'. + rv_units = 3500. + WHEN 'PREM'. + rv_units = 6000. + WHEN 'BED'. + rv_units = 9000. + ENDCASE. + ENDMETHOD. + + METHOD to_amount. + DATA(lv_cents) = CONV ty_amount( iv_cents ). + rv_rent = lv_cents * c_cent_factor. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/faulty/m1_garden_rent.clas.abap b/tasks_gen/train/G1055/faulty/m1_garden_rent.clas.abap new file mode 100644 index 0000000..2b6e03f --- /dev/null +++ b/tasks_gen/train/G1055/faulty/m1_garden_rent.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}garden_rent DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plot_type TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + rent TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + plot TYPE ty_reason VALUE 'PLOT', + area TYPE ty_reason VALUE 'AREA', + END OF c_reason. + METHODS calculate + IMPORTING iv_plot_type TYPE ty_plot_type + iv_area TYPE i + iv_water TYPE abap_bool + iv_member TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_min_area TYPE i VALUE 10, + c_max_area TYPE i VALUE 500, + c_water_units TYPE i VALUE 120000, + c_member_percent TYPE i VALUE 85, + c_scale TYPE i VALUE 100, + c_half_cent TYPE i VALUE 50, + c_min_rent_cents TYPE i VALUE 1000, + c_cent_factor TYPE ty_amount VALUE '0.01'. + + METHODS is_valid_plot_type + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS rate_units + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_units) TYPE i. + METHODS to_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_rent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}garden_rent IMPLEMENTATION. + METHOD calculate. + IF is_valid_plot_type( iv_plot_type ) = abap_false. + rs_result-reason = c_reason-plot. + RETURN. + ENDIF. + IF iv_area < c_min_area OR iv_area > c_max_area. + rs_result-reason = c_reason-area. + RETURN. + ENDIF. + DATA(lv_units) = iv_area * rate_units( iv_plot_type ). + IF iv_water <> abap_true. + lv_units = lv_units + c_water_units. + ENDIF. + IF iv_member = abap_true. + lv_units = ( lv_units * c_member_percent ) DIV c_scale. + ENDIF. + DATA(lv_cents) = ( lv_units + c_half_cent ) DIV c_scale. + IF lv_cents < c_min_rent_cents. + lv_cents = c_min_rent_cents. + ENDIF. + rs_result-rent = to_amount( lv_cents ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD is_valid_plot_type. + CASE to_upper( iv_plot_type ). + WHEN 'STD' OR 'PREM' OR 'BED'. + rv_valid = abap_true. + ENDCASE. + ENDMETHOD. + + METHOD rate_units. + CASE to_upper( iv_plot_type ). + WHEN 'STD'. + rv_units = 3500. + WHEN 'PREM'. + rv_units = 6000. + WHEN 'BED'. + rv_units = 9000. + ENDCASE. + ENDMETHOD. + + METHOD to_amount. + DATA(lv_cents) = CONV ty_amount( iv_cents ). + rv_rent = lv_cents * c_cent_factor. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/faulty/m2_garden_rent.clas.abap b/tasks_gen/train/G1055/faulty/m2_garden_rent.clas.abap new file mode 100644 index 0000000..55abb80 --- /dev/null +++ b/tasks_gen/train/G1055/faulty/m2_garden_rent.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}garden_rent DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plot_type TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + rent TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + plot TYPE ty_reason VALUE 'PLOT', + area TYPE ty_reason VALUE 'AREA', + END OF c_reason. + METHODS calculate + IMPORTING iv_plot_type TYPE ty_plot_type + iv_area TYPE i + iv_water TYPE abap_bool + iv_member TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_min_area TYPE i VALUE 10, + c_max_area TYPE i VALUE 500, + c_water_units TYPE i VALUE 120000, + c_member_percent TYPE i VALUE 85, + c_scale TYPE i VALUE 100, + c_half_cent TYPE i VALUE 50, + c_min_rent_cents TYPE i VALUE 1000, + c_cent_factor TYPE ty_amount VALUE '0.01'. + + METHODS is_valid_plot_type + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS rate_units + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_units) TYPE i. + METHODS to_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_rent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}garden_rent IMPLEMENTATION. + METHOD calculate. + IF is_valid_plot_type( iv_plot_type ) = abap_false. + rs_result-reason = c_reason-plot. + RETURN. + ENDIF. + IF iv_area < c_min_area OR iv_area > c_max_area. + rs_result-reason = c_reason-area. + RETURN. + ENDIF. + DATA(lv_units) = iv_area * rate_units( iv_plot_type ). + IF iv_water = abap_true. + lv_units = lv_units + c_water_units. + ENDIF. + IF iv_member = abap_false. + lv_units = ( lv_units * c_member_percent ) DIV c_scale. + ENDIF. + DATA(lv_cents) = ( lv_units + c_half_cent ) DIV c_scale. + IF lv_cents < c_min_rent_cents. + lv_cents = c_min_rent_cents. + ENDIF. + rs_result-rent = to_amount( lv_cents ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD is_valid_plot_type. + CASE to_upper( iv_plot_type ). + WHEN 'STD' OR 'PREM' OR 'BED'. + rv_valid = abap_true. + ENDCASE. + ENDMETHOD. + + METHOD rate_units. + CASE to_upper( iv_plot_type ). + WHEN 'STD'. + rv_units = 3500. + WHEN 'PREM'. + rv_units = 6000. + WHEN 'BED'. + rv_units = 9000. + ENDCASE. + ENDMETHOD. + + METHOD to_amount. + DATA(lv_cents) = CONV ty_amount( iv_cents ). + rv_rent = lv_cents * c_cent_factor. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/faulty/m3_garden_rent.clas.abap b/tasks_gen/train/G1055/faulty/m3_garden_rent.clas.abap new file mode 100644 index 0000000..336a3a8 --- /dev/null +++ b/tasks_gen/train/G1055/faulty/m3_garden_rent.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}garden_rent DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plot_type TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + rent TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + plot TYPE ty_reason VALUE 'PLOT', + area TYPE ty_reason VALUE 'AREA', + END OF c_reason. + METHODS calculate + IMPORTING iv_plot_type TYPE ty_plot_type + iv_area TYPE i + iv_water TYPE abap_bool + iv_member TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_min_area TYPE i VALUE 10, + c_max_area TYPE i VALUE 500, + c_water_units TYPE i VALUE 120000, + c_member_percent TYPE i VALUE 85, + c_scale TYPE i VALUE 100, + c_half_cent TYPE i VALUE 50, + c_min_rent_cents TYPE i VALUE 1000, + c_cent_factor TYPE ty_amount VALUE '0.01'. + + METHODS is_valid_plot_type + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS rate_units + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_units) TYPE i. + METHODS to_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_rent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}garden_rent IMPLEMENTATION. + METHOD calculate. + IF is_valid_plot_type( iv_plot_type ) = abap_false. + rs_result-reason = c_reason-plot. + RETURN. + ENDIF. + IF iv_area < c_min_area OR iv_area > c_max_area. + rs_result-reason = c_reason-area. + RETURN. + ENDIF. + DATA(lv_units) = iv_area * rate_units( iv_plot_type ). + IF iv_water = abap_true. + lv_units = lv_units + c_water_units. + ENDIF. + IF iv_member = abap_true. + lv_units = ( lv_units / c_member_percent ) DIV c_scale. + ENDIF. + DATA(lv_cents) = ( lv_units + c_half_cent ) DIV c_scale. + IF lv_cents < c_min_rent_cents. + lv_cents = c_min_rent_cents. + ENDIF. + rs_result-rent = to_amount( lv_cents ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD is_valid_plot_type. + CASE to_upper( iv_plot_type ). + WHEN 'STD' OR 'PREM' OR 'BED'. + rv_valid = abap_true. + ENDCASE. + ENDMETHOD. + + METHOD rate_units. + CASE to_upper( iv_plot_type ). + WHEN 'STD'. + rv_units = 3500. + WHEN 'PREM'. + rv_units = 6000. + WHEN 'BED'. + rv_units = 9000. + ENDCASE. + ENDMETHOD. + + METHOD to_amount. + DATA(lv_cents) = CONV ty_amount( iv_cents ). + rv_rent = lv_cents * c_cent_factor. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/generation.json b/tasks_gen/train/G1055/generation.json new file mode 100644 index 0000000..a9e8abe --- /dev/null +++ b/tasks_gen/train/G1055/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G1055", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G1038 (similarity spec 0.53, rules 0.66, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 88.5, + "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/G1055/hidden/t02_garden_hidden.clas.abap b/tasks_gen/train/G1055/hidden/t02_garden_hidden.clas.abap new file mode 100644 index 0000000..733a803 --- /dev/null +++ b/tasks_gen/train/G1055/hidden/t02_garden_hidden.clas.abap @@ -0,0 +1,104 @@ +CLASS {{p}}t02_garden_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + CONSTANTS: + c_0000 TYPE ty_amount VALUE '0.00', + c_1000 TYPE ty_amount VALUE '10.00', + c_2270 TYPE ty_amount VALUE '22.70', + c_2550 TYPE ty_amount VALUE '25.50', + c_3500 TYPE ty_amount VALUE '35.00', + c_6000 TYPE ty_amount VALUE '60.00', + c_7200 TYPE ty_amount VALUE '72.00'. + DATA mo_cut TYPE REF TO {{p}}garden_rent. + METHODS setup. + METHODS standard_plot_rent FOR TESTING. + METHODS raised_bed_member FOR TESTING. + METHODS premium_plot_water FOR TESTING. + METHODS minimum_rent_applies FOR TESTING. + METHODS rounds_half_up FOR TESTING. + METHODS lower_case_plot_type FOR TESTING. + METHODS unknown_plot_type FOR TESTING. + METHODS area_too_small FOR TESTING. + METHODS area_too_large FOR TESTING. + METHODS plot_checked_first FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_garden_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}garden_rent( ). + ENDMETHOD. + + METHOD standard_plot_rent. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 100 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = c_3500 act = ls_result-rent ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD raised_bed_member. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'BED' iv_area = 20 + iv_water = abap_true iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = c_2550 act = ls_result-rent ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD premium_plot_water. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'PREM' iv_area = 100 + iv_water = abap_true iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = c_7200 act = ls_result-rent ). + ENDMETHOD. + + METHOD minimum_rent_applies. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 10 + iv_water = abap_false iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = c_1000 act = ls_result-rent ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD rounds_half_up. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 42 + iv_water = abap_true iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = c_2270 act = ls_result-rent ). + ENDMETHOD. + + METHOD lower_case_plot_type. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'prem' iv_area = 100 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = c_6000 act = ls_result-rent ). + ENDMETHOD. + + METHOD unknown_plot_type. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'X' iv_area = 100 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-plot + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_0000 act = ls_result-rent ). + ENDMETHOD. + + METHOD area_too_small. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 9 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-area + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = c_0000 act = ls_result-rent ). + ENDMETHOD. + + METHOD area_too_large. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 501 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-area + act = ls_result-reason ). + ENDMETHOD. + + METHOD plot_checked_first. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'X' iv_area = 0 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-plot + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/mutation.json b/tasks_gen/train/G1055/mutation.json new file mode 100644 index 0000000..1459587 --- /dev/null +++ b/tasks_gen/train/G1055/mutation.json @@ -0,0 +1,68 @@ +{ + "task": "G1055", + "mutants": [ + { + "object": "{{P}}GARDEN_RENT", + "mutant": "line 53: > -> <= (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "AREA_TOO_LARGE", + "LOWER_CASE_PLOT_TYPE", + "MINIMUM_RENT_APPLIES", + "PREMIUM_PLOT_WATER", + "RAISED_BED_MEMBER", + "ROUNDS_HALF_UP", + "STANDARD_PLOT_RENT" + ] + }, + { + "object": "{{P}}GARDEN_RENT", + "mutant": "line 58: = -> <> (eq)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "LOWER_CASE_PLOT_TYPE", + "MINIMUM_RENT_APPLIES", + "PREMIUM_PLOT_WATER", + "RAISED_BED_MEMBER", + "ROUNDS_HALF_UP", + "STANDARD_PLOT_RENT" + ] + }, + { + "object": "{{P}}GARDEN_RENT", + "mutant": "line 61: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "5/10", + "failed_tests": [ + "LOWER_CASE_PLOT_TYPE", + "PREMIUM_PLOT_WATER", + "RAISED_BED_MEMBER", + "ROUNDS_HALF_UP", + "STANDARD_PLOT_RENT" + ] + }, + { + "object": "{{P}}GARDEN_RENT", + "mutant": "line 62: * -> / (arith)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "RAISED_BED_MEMBER", + "ROUNDS_HALF_UP" + ] + }, + { + "object": "{{P}}GARDEN_RENT", + "mutant": "line 74: OR -> AND (logic)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1055/reference/garden_rent.clas.abap b/tasks_gen/train/G1055/reference/garden_rent.clas.abap new file mode 100644 index 0000000..ff1ad21 --- /dev/null +++ b/tasks_gen/train/G1055/reference/garden_rent.clas.abap @@ -0,0 +1,94 @@ +CLASS {{p}}garden_rent DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plot_type TYPE c LENGTH 4. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + rent TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + plot TYPE ty_reason VALUE 'PLOT', + area TYPE ty_reason VALUE 'AREA', + END OF c_reason. + METHODS calculate + IMPORTING iv_plot_type TYPE ty_plot_type + iv_area TYPE i + iv_water TYPE abap_bool + iv_member TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_min_area TYPE i VALUE 10, + c_max_area TYPE i VALUE 500, + c_water_units TYPE i VALUE 120000, + c_member_percent TYPE i VALUE 85, + c_scale TYPE i VALUE 100, + c_half_cent TYPE i VALUE 50, + c_min_rent_cents TYPE i VALUE 1000, + c_cent_factor TYPE ty_amount VALUE '0.01'. + + METHODS is_valid_plot_type + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS rate_units + IMPORTING iv_plot_type TYPE ty_plot_type + RETURNING VALUE(rv_units) TYPE i. + METHODS to_amount + IMPORTING iv_cents TYPE i + RETURNING VALUE(rv_rent) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}garden_rent IMPLEMENTATION. + METHOD calculate. + IF is_valid_plot_type( iv_plot_type ) = abap_false. + rs_result-reason = c_reason-plot. + RETURN. + ENDIF. + IF iv_area < c_min_area OR iv_area > c_max_area. + rs_result-reason = c_reason-area. + RETURN. + ENDIF. + DATA(lv_units) = iv_area * rate_units( iv_plot_type ). + IF iv_water = abap_true. + lv_units = lv_units + c_water_units. + ENDIF. + IF iv_member = abap_true. + lv_units = ( lv_units * c_member_percent ) DIV c_scale. + ENDIF. + DATA(lv_cents) = ( lv_units + c_half_cent ) DIV c_scale. + IF lv_cents < c_min_rent_cents. + lv_cents = c_min_rent_cents. + ENDIF. + rs_result-rent = to_amount( lv_cents ). + rs_result-reason = c_reason-ok. + ENDMETHOD. + + METHOD is_valid_plot_type. + CASE to_upper( iv_plot_type ). + WHEN 'STD' OR 'PREM' OR 'BED'. + rv_valid = abap_true. + ENDCASE. + ENDMETHOD. + + METHOD rate_units. + CASE to_upper( iv_plot_type ). + WHEN 'STD'. + rv_units = 3500. + WHEN 'PREM'. + rv_units = 6000. + WHEN 'BED'. + rv_units = 9000. + ENDCASE. + ENDMETHOD. + + METHOD to_amount. + DATA(lv_cents) = CONV ty_amount( iv_cents ). + rv_rent = lv_cents * c_cent_factor. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/reference/garden_rent.testclasses.abap b/tasks_gen/train/G1055/reference/garden_rent.testclasses.abap new file mode 100644 index 0000000..a2fe93c --- /dev/null +++ b/tasks_gen/train/G1055/reference/garden_rent.testclasses.abap @@ -0,0 +1,71 @@ +CLASS ltc_garden_rent 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}}garden_rent. + METHODS setup. + METHODS standard_plot_rent FOR TESTING. + METHODS member_discount FOR TESTING. + METHODS water_connection_fee FOR TESTING. + METHODS minimum_rent FOR TESTING. + METHODS rounds_half_up FOR TESTING. + METHODS unknown_plot_type FOR TESTING. + METHODS area_out_of_range FOR TESTING. +ENDCLASS. + + +CLASS ltc_garden_rent IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}garden_rent( ). + ENDMETHOD. + + METHOD standard_plot_rent. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 100 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '35.00' ) act = ls_result-rent ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-ok + act = ls_result-reason ). + ENDMETHOD. + + METHOD member_discount. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 100 + iv_water = abap_false iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '29.75' ) act = ls_result-rent ). + ENDMETHOD. + + METHOD water_connection_fee. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'PREM' iv_area = 100 + iv_water = abap_true iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '72.00' ) act = ls_result-rent ). + ENDMETHOD. + + METHOD minimum_rent. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 10 + iv_water = abap_false iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '10.00' ) act = ls_result-rent ). + ENDMETHOD. + + METHOD rounds_half_up. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 42 + iv_water = abap_true iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '22.70' ) act = ls_result-rent ). + ENDMETHOD. + + METHOD unknown_plot_type. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'X' iv_area = 100 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-plot + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' ) act = ls_result-rent ). + ENDMETHOD. + + METHOD area_out_of_range. + DATA(ls_result) = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 9 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-area + act = ls_result-reason ). + ls_result = mo_cut->calculate( iv_plot_type = 'STD' iv_area = 501 + iv_water = abap_false iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}garden_rent=>c_reason-area + act = ls_result-reason ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1055/spec.md b/tasks_gen/train/G1055/spec.md new file mode 100644 index 0000000..1407c8c --- /dev/null +++ b/tasks_gen/train/G1055/spec.md @@ -0,0 +1,61 @@ +# 1. Goal +A community garden association rents garden plots to its tenants and charges an +annual rent for each plot. The billing program of the association calls one +calculation for each plot. The program needs the rent and one reason code for +each plot. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}GARDEN_RENT does not exist. Create it in package $TMP. +- The billing program calls the class for each plot. It passes the plot type, the + area of the plot in square metres, a flag that tells if the plot has a water + connection, and a flag that tells if the tenant is a member of the association. + +# 4. Contract +- Create the class {{P}}GARDEN_RENT in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- Do not add other public methods. +- Define these public types: + - TY_PLOT_TYPE: type c, length 4. + - TY_REASON: type c, length 10. + - TY_AMOUNT: type p, length 8, 2 decimals. + - TY_RESULT: structure with the components REASON (type TY_REASON) and RENT + (type TY_AMOUNT). +- Define the public constant structure C_REASON with the components OK, PLOT and + AREA. The values are 'OK', 'PLOT' and 'AREA'. +- The class has the public method CALCULATE with these parameters: + - importing IV_PLOT_TYPE, type TY_PLOT_TYPE. + - importing IV_AREA, type i. + - importing IV_WATER, type abap_bool. + - importing IV_MEMBER, type abap_bool. + - returning RS_RESULT, type TY_RESULT. + +# 5. Business rules +Apply the rules in this sequence. The first rule that fails decides the result. +1. Convert the plot type to upper case. +2. The plot type must be STD, PREM or BED. If not, return the reason PLOT and the + rent 0.00. +3. The area must be between 10 and 500 square metres. Both limits are included. + If not, return the reason AREA and the rent 0.00. +4. The rent per square metre depends on the plot type: STD = 0.35, PREM = 0.60, + BED = 0.90. +5. The rent is the area multiplied by the rent per square metre. +6. If the plot has a water connection, add 12.00. +7. If the tenant is a member of the association, subtract 15 percent of that + amount. +8. Round the rent to two decimals. Round half up: 22.695 becomes 22.70. +9. The rent must be at least 10.00. If it is less, use 10.00. +10. Return the reason OK and the rent. + +# 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 create other objects and do not use a database table. + +# 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/G1055/task.json b/tasks_gen/train/G1055/task.json new file mode 100644 index 0000000..55be6aa --- /dev/null +++ b/tasks_gen/train/G1055/task.json @@ -0,0 +1,41 @@ +{ + "id": "G1055", + "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}}GARDEN_RENT" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_GARDEN_HIDDEN", + "file": "hidden/t02_garden_hidden.clas.abap", + "description": "T02 hidden tests for the annual rent of a community garden plot" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}GARDEN_RENT", + "file": "reference/garden_rent.clas.abap", + "description": "Annual rent of a community garden plot", + "testclasses_file": "reference/garden_rent.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/train/G1300/generation.json b/tasks_gen/train/G1300/generation.json new file mode 100644 index 0000000..089e679 --- /dev/null +++ b/tasks_gen/train/G1300/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1300", + "pool": "train", + "category": "K", + "base_task": "G1000", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1300/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1300/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..6a36d64 --- /dev/null +++ b/tasks_gen/train/G1300/hidden/t20_hidden.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}t20_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 deviation_below FOR TESTING. + METHODS deviation_zero FOR TESTING. + METHODS deviation_above FOR TESTING. + METHODS percent_rounded FOR TESTING. + METHODS status_hold FOR TESTING. + METHODS status_warn FOR TESTING. + METHODS status_load FOR TESTING. + METHODS status_at_five_pct FOR TESTING. + METHODS unweighed_hidden FOR TESTING. + METHODS customer_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CONT_WEIGHT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_cont TYPE STANDARD TABLE OF {{p}}cont WITH EMPTY KEY. + DATA lt_weigh TYPE STANDARD TABLE OF {{p}}weigh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_cont = VALUE #( + ( cont_id = 'C1' customer = 'ALPHA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C2' customer = 'BETA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C3' customer = 'GAMMA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C4' customer = 'DELTA' decl_kg = '500.00' max_kg = '600.00' ) + ( cont_id = 'C5' customer = 'EPSILON' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C6' customer = 'ZETA' decl_kg = '1000.00' max_kg = '1200.00' ) + ( cont_id = 'C7' customer = 'ETA' decl_kg = '800.00' max_kg = '900.00' ) + ( cont_id = 'C8' customer = 'THETA' decl_kg = '300.00' max_kg = '400.00' ) ). + lt_weigh = VALUE #( + ( cont_id = 'C1' meas_kg = '1020.00' ) + ( cont_id = 'C2' meas_kg = '1100.00' ) + ( cont_id = 'C3' meas_kg = '1300.00' ) + ( cont_id = 'C4' meas_kg = '450.00' ) + ( cont_id = 'C5' meas_kg = '950.00' ) + ( cont_id = 'C7' meas_kg = '800.00' ) + ( cont_id = 'C8' meas_kg = '310.00' ) ). + go_env->insert_test_data( lt_cont ). + go_env->insert_test_data( lt_weigh ). + ENDMETHOD. + + METHOD deviation_below. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C4' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '50.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD deviation_zero. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C7' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD deviation_above. + SELECT SINGLE deviation_kg FROM {{p}}i_cont_weight WHERE cont_id = 'C1' INTO @DATA(lv_dev). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.00' ) + act = CONV decfloat34( lv_dev ) ). + ENDMETHOD. + + METHOD percent_rounded. + SELECT SINGLE deviation_pct FROM {{p}}i_cont_weight WHERE cont_id = 'C8' INTO @DATA(lv_pct). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '3.33' ) + act = CONV decfloat34( lv_pct ) ). + ENDMETHOD. + + METHOD status_hold. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C3' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'HOLD' act = lv_status ). + ENDMETHOD. + + METHOD status_warn. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C2' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'WARN' act = lv_status ). + ENDMETHOD. + + METHOD status_load. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C1' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = lv_status ). + ENDMETHOD. + + METHOD status_at_five_pct. + SELECT SINGLE status FROM {{p}}i_cont_weight WHERE cont_id = 'C5' INTO @DATA(lv_status). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = lv_status ). + ENDMETHOD. + + METHOD unweighed_hidden. + SELECT COUNT(*) FROM {{p}}i_cont_weight INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 7 act = lv_rows ). + ENDMETHOD. + + METHOD customer_shown. + SELECT SINGLE customer FROM {{p}}i_cont_weight WHERE cont_id = 'C4' INTO @DATA(lv_cust). + cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = lv_cust ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1300/reference/i_cont_weight.ddls.asddls b/tasks_gen/train/G1300/reference/i_cont_weight.ddls.asddls new file mode 100644 index 0000000..db6495d --- /dev/null +++ b/tasks_gen/train/G1300/reference/i_cont_weight.ddls.asddls @@ -0,0 +1,18 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Container weight check' +define view entity {{P}}I_CONT_WEIGHT + as select from {{p}}cont as cont + inner join {{p}}weigh as weigh on weigh.cont_id = cont.cont_id +{ + key cont.cont_id as cont_id, + cont.customer as customer, + cont.decl_kg as decl_kg, + weigh.meas_kg as meas_kg, + cast( abs( weigh.meas_kg - cont.decl_kg ) as abap.dec(15,2) ) as deviation_kg, + division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) as deviation_pct, + case + when weigh.meas_kg > cont.max_kg then 'HOLD' + when division( abs( weigh.meas_kg - cont.decl_kg ) * 100, cont.decl_kg, 2 ) > 5 then 'WARN' + else 'LOAD' + end as status +} diff --git a/tasks_gen/train/G1300/reference/t20_test.clas.abap b/tasks_gen/train/G1300/reference/t20_test.clas.abap new file mode 100644 index 0000000..bdc4ffd --- /dev/null +++ b/tasks_gen/train/G1300/reference/t20_test.clas.abap @@ -0,0 +1,53 @@ +CLASS {{p}}t20_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 hold_for_heavy FOR TESTING. + METHODS load_for_light FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CONT_WEIGHT' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_cont TYPE STANDARD TABLE OF {{p}}cont WITH EMPTY KEY. + DATA lt_weigh TYPE STANDARD TABLE OF {{p}}weigh WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_cont = VALUE #( ( cont_id = 'A' customer = 'CUST_A' decl_kg = '1000.00' max_kg = '1100.00' ) + ( cont_id = 'B' customer = 'CUST_B' decl_kg = '1000.00' max_kg = '1100.00' ) ). + lt_weigh = VALUE #( ( cont_id = 'A' meas_kg = '1150.00' ) + ( cont_id = 'B' meas_kg = '1010.00' ) ). + go_env->insert_test_data( lt_cont ). + go_env->insert_test_data( lt_weigh ). + ENDMETHOD. + + METHOD hold_for_heavy. + SELECT SINGLE deviation_kg, deviation_pct, status FROM {{p}}i_cont_weight + WHERE cont_id = 'A' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '150.00' ) + act = CONV decfloat34( ls_row-deviation_kg ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '15.00' ) + act = CONV decfloat34( ls_row-deviation_pct ) ). + cl_abap_unit_assert=>assert_equals( exp = 'HOLD' act = ls_row-status ). + ENDMETHOD. + + METHOD load_for_light. + SELECT SINGLE deviation_kg, deviation_pct, status FROM {{p}}i_cont_weight + WHERE cont_id = 'B' INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) + act = CONV decfloat34( ls_row-deviation_kg ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) + act = CONV decfloat34( ls_row-deviation_pct ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOAD' act = ls_row-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1300/seed/cont.tabl.asabap b/tasks_gen/train/G1300/seed/cont.tabl.asabap new file mode 100644 index 0000000..d513411 --- /dev/null +++ b/tasks_gen/train/G1300/seed/cont.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Container' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}cont { + key client : abap.clnt not null; + key cont_id : abap.char(10) not null; + customer : abap.char(10); + decl_kg : abap.dec(15,2); + max_kg : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1300/seed/weigh.tabl.asabap b/tasks_gen/train/G1300/seed/weigh.tabl.asabap new file mode 100644 index 0000000..8b2b887 --- /dev/null +++ b/tasks_gen/train/G1300/seed/weigh.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Weighbridge record' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}weigh { + key client : abap.clnt not null; + key cont_id : abap.char(10) not null; + meas_kg : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1300/spec.md b/tasks_gen/train/G1300/spec.md new file mode 100644 index 0000000..34a5138 --- /dev/null +++ b/tasks_gen/train/G1300/spec.md @@ -0,0 +1 @@ +Please create the CDS view entity {{P}}I_CONT_WEIGHT in package $TMP. The view shows the weight check for each container at a container terminal. A report and an OData service will read this check. Therefore the calculation must be in the database, in a CDS view. The view uses table {{P}}CONT (container master) in package $TMP. Its key is CONT_ID (CHAR 10). Its fields are CUSTOMER (CHAR 10), DECL_KG (DEC 15,2, declared weight in kilograms), and MAX_KG (DEC 15,2, maximum permitted weight in kilograms). The view also uses table {{P}}WEIGH (weighbridge record) in package $TMP. Its key is CONT_ID (CHAR 10). Its field is MEAS_KG (DEC 15,2, measured weight in kilograms). The view elements have these names: CONT_ID (key), CUSTOMER, DECL_KG, MEAS_KG, DEVIATION_KG, DEVIATION_PCT, and STATUS. The view has no authorization check (#NOT_REQUIRED). Show one row for each container that has a weighbridge record. Do not show a container without a weighbridge record. DEVIATION_KG is the difference between MEAS_KG and DECL_KG, without a sign. It is always positive or zero and has 2 decimals. DEVIATION_PCT is DEVIATION_KG as a percentage of DECL_KG. It has 2 decimals. STATUS is 'HOLD' if MEAS_KG is greater than MAX_KG. STATUS is 'WARN' if MEAS_KG is not greater than MAX_KG and DEVIATION_PCT is greater than 5. STATUS is 'LOAD' in all other cases. DECL_KG is always greater than zero. The release target is 8.16. Use Clean ABAP. Keep every method below 40 statements. Do not change {{P}}CONT and {{P}}WEIGH. The view must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1300/task.json b/tasks_gen/train/G1300/task.json new file mode 100644 index 0000000..ab1fa2a --- /dev/null +++ b/tasks_gen/train/G1300/task.json @@ -0,0 +1,70 @@ +{ + "id": "G1300", + "category": "K", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}CONT", + "file": "seed/cont.tabl.asabap", + "description": "Container master" + }, + { + "type": "TABL", + "name": "{{P}}WEIGH", + "file": "seed/weigh.tabl.asabap", + "description": "Weighbridge record" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_CONT_WEIGHT", + "fields": [ + "CONT_ID", + "CUSTOMER", + "DECL_KG", + "MEAS_KG", + "DEVIATION_KG", + "DEVIATION_PCT", + "STATUS" + ] + } + ], + "out_of_scope": [ + "{{P}}CONT", + "{{P}}WEIGH" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "T20 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_CONT_WEIGHT", + "file": "reference/i_cont_weight.ddls.asddls", + "description": "Container weight check" + }, + { + "type": "CLAS", + "name": "{{P}}T20_TEST", + "file": "reference/t20_test.clas.abap", + "description": "T20 own tests" + } + ], + "craft_checks": [], + "base_task": "G1000", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1301/generation.json b/tasks_gen/train/G1301/generation.json new file mode 100644 index 0000000..bc43995 --- /dev/null +++ b/tasks_gen/train/G1301/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1301", + "pool": "train", + "category": "K", + "base_task": "G1001", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1301/hidden/harvest_hidden.clas.abap b/tasks_gen/train/G1301/hidden/harvest_hidden.clas.abap new file mode 100644 index 0000000..db6996f --- /dev/null +++ b/tasks_gen/train/G1301/hidden/harvest_hidden.clas.abap @@ -0,0 +1,193 @@ +CLASS {{p}}harvest_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}harvest_evaluator. + + METHODS setup. + METHODS assert_rows + IMPORTING it_act TYPE {{p}}if_harvest=>tt_result + it_exp TYPE {{p}}if_harvest=>tt_result + iv_msg TYPE string. + + METHODS empty_input FOR TESTING. + METHODS unknown_member_ignored FOR TESTING. + METHODS zero_quantity_ignored FOR TESTING. + METHODS negative_quantity_ignored FOR TESTING. + METHODS same_member_counts_once FOR TESTING. + METHODS two_varieties_separated FOR TESTING. + METHODS sorted_by_total_desc FOR TESTING. + METHODS tie_broken_by_variety FOR TESTING. + METHODS all_reports_ignored FOR TESTING. + METHODS member_without_report FOR TESTING. +ENDCLASS. + + +CLASS {{p}}harvest_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}harvest_evaluator( ). + ENDMETHOD. + + METHOD assert_rows. + cl_abap_unit_assert=>assert_equals( exp = lines( it_exp ) + act = lines( it_act ) + msg = |{ iv_msg }: number of rows| ). + LOOP AT it_exp ASSIGNING FIELD-SYMBOL(). + READ TABLE it_act ASSIGNING FIELD-SYMBOL() INDEX sy-tabix. + IF sy-subrc <> 0. + RETURN. + ENDIF. + cl_abap_unit_assert=>assert_equals( exp = -variety + act = -variety + msg = |{ iv_msg }: variety| ). + cl_abap_unit_assert=>assert_equals( exp = -total_kg + act = -total_kg + msg = |{ iv_msg }: total_kg| ). + cl_abap_unit_assert=>assert_equals( exp = -member_count + act = -member_count + msg = |{ iv_msg }: member_count| ). + ENDLOOP. + ENDMETHOD. + + METHOD empty_input. + DATA lt_report TYPE {{p}}if_harvest=>tt_report. + DATA lt_member TYPE {{p}}if_harvest=>tt_member. + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD unknown_member_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 100 ) + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 50 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 100 member_count = 1 ) ) + iv_msg = 'unknown member' ). + ENDMETHOD. + + METHOD zero_quantity_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 30 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 30 member_count = 1 ) ) + iv_msg = 'zero quantity' ). + ENDMETHOD. + + METHOD negative_quantity_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = -5 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 20 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 20 member_count = 1 ) ) + iv_msg = 'negative quantity' ). + ENDMETHOD. + + METHOD same_member_counts_once. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 20 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 35 member_count = 2 ) ) + iv_msg = 'same member' ). + ENDMETHOD. + + METHOD two_varieties_separated. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = 7 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 20 member_count = 2 ) + ( variety = 'SPAE' total_kg = 7 member_count = 1 ) ) + iv_msg = 'two varieties' ). + ENDMETHOD. + + METHOD sorted_by_total_desc. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) + ( member_id = 'M00004' member_name = 'Hof Eiche' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'SPAE' quantity_kg = 30 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 20 ) + ( member_id = 'M00004' variety = 'SPAE' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'SPAE' total_kg = 35 member_count = 2 ) + ( variety = 'SILV' total_kg = 20 member_count = 1 ) + ( variety = 'RIES' total_kg = 10 member_count = 1 ) ) + iv_msg = 'sort by total' ). + ENDMETHOD. + + METHOD tie_broken_by_variety. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = 10 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 10 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 10 member_count = 1 ) + ( variety = 'SILV' total_kg = 10 member_count = 1 ) + ( variety = 'SPAE' total_kg = 10 member_count = 1 ) ) + iv_msg = 'tie by variety' ). + ENDMETHOD. + + METHOD all_reports_ignored. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'SPAE' quantity_kg = -3 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD member_without_report. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + assert_rows( it_act = lt_result + it_exp = VALUE #( ( variety = 'RIES' total_kg = 5 member_count = 1 ) ) + iv_msg = 'member without report' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1301/reference/harvest_evaluator.clas.abap b/tasks_gen/train/G1301/reference/harvest_evaluator.clas.abap new file mode 100644 index 0000000..edc0251 --- /dev/null +++ b/tasks_gen/train/G1301/reference/harvest_evaluator.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}harvest_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS evaluate + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE {{p}}if_harvest=>tt_member + RETURNING VALUE(rt_result) TYPE {{p}}if_harvest=>tt_result. + + PRIVATE SECTION. + TYPES tt_member_key TYPE HASHED TABLE OF {{p}}if_harvest=>ty_member + WITH UNIQUE KEY member_id. + TYPES tt_member_id TYPE SORTED TABLE OF {{p}}if_harvest=>ty_member_id + WITH UNIQUE KEY table_line. + TYPES tt_report_key TYPE SORTED TABLE OF {{p}}if_harvest=>ty_report + WITH NON-UNIQUE KEY variety member_id. + + METHODS countable_reports + IMPORTING it_report TYPE {{p}}if_harvest=>tt_report + it_member TYPE tt_member_key + RETURNING VALUE(rt_report) TYPE tt_report_key. + METHODS is_countable + IMPORTING is_report TYPE {{p}}if_harvest=>ty_report + it_member TYPE tt_member_key + RETURNING VALUE(rv_count) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}harvest_evaluator IMPLEMENTATION. + METHOD evaluate. + DATA lt_member TYPE tt_member_key. + lt_member = it_member. + + DATA(lt_report) = countable_reports( it_report = it_report + it_member = lt_member ). + + DATA lt_result TYPE {{p}}if_harvest=>tt_result. + DATA lt_member_id TYPE tt_member_id. + LOOP AT lt_report ASSIGNING FIELD-SYMBOL() GROUP BY -variety. + DATA(lv_variety) = -variety. + DATA(lv_total) = 0. + CLEAR lt_member_id. + LOOP AT GROUP ASSIGNING FIELD-SYMBOL(). + lv_total = lv_total + -quantity_kg. + INSERT -member_id INTO TABLE lt_member_id. + ENDLOOP. + APPEND VALUE {{p}}if_harvest=>ty_result( + variety = lv_variety + total_kg = lv_total + member_count = lines( lt_member_id ) ) TO lt_result. + ENDLOOP. + + SORT lt_result BY total_kg DESCENDING variety ASCENDING. + rt_result = lt_result. + ENDMETHOD. + + METHOD countable_reports. + LOOP AT it_report INTO DATA(ls_report). + IF is_countable( is_report = ls_report + it_member = it_member ) = abap_false. + CONTINUE. + ENDIF. + INSERT ls_report INTO TABLE rt_report. + ENDLOOP. + ENDMETHOD. + + METHOD is_countable. + IF is_report-quantity_kg <= 0. + RETURN. + ENDIF. + rv_count = xsdbool( line_exists( it_member[ member_id = is_report-member_id ] ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1301/reference/harvest_evaluator.testclasses.abap b/tasks_gen/train/G1301/reference/harvest_evaluator.testclasses.abap new file mode 100644 index 0000000..86dbb70 --- /dev/null +++ b/tasks_gen/train/G1301/reference/harvest_evaluator.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_harvest_evaluator DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}harvest_evaluator. + METHODS setup. + METHODS sums_quantities FOR TESTING. + METHODS counts_members_once FOR TESTING. + METHODS ignores_unknown_member FOR TESTING. + METHODS ignores_zero_quantity FOR TESTING. + METHODS sorts_by_total FOR TESTING. +ENDCLASS. + + +CLASS ltc_harvest_evaluator IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}harvest_evaluator( ). + ENDMETHOD. + + METHOD sums_quantities. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 15 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 25 act = lt_result[ 1 ]-total_kg ). + ENDMETHOD. + + METHOD counts_members_once. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) + ( member_id = 'M00002' variety = 'RIES' quantity_kg = 5 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_result[ 1 ]-total_kg ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD ignores_unknown_member. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 10 ) + ( member_id = 'M00009' variety = 'RIES' quantity_kg = 99 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_kg ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-member_count ). + ENDMETHOD. + + METHOD ignores_zero_quantity. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 0 ) + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 40 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 40 act = lt_result[ 1 ]-total_kg ). + ENDMETHOD. + + METHOD sorts_by_total. + DATA(lt_member) = VALUE {{p}}if_harvest=>tt_member( + ( member_id = 'M00001' member_name = 'Hof Adler' ) + ( member_id = 'M00002' member_name = 'Hof Birke' ) + ( member_id = 'M00003' member_name = 'Hof Zeder' ) ). + DATA(lt_report) = VALUE {{p}}if_harvest=>tt_report( + ( member_id = 'M00001' variety = 'RIES' quantity_kg = 5 ) + ( member_id = 'M00002' variety = 'SPAE' quantity_kg = 30 ) + ( member_id = 'M00003' variety = 'SILV' quantity_kg = 20 ) ). + DATA(lt_result) = mo_cut->evaluate( it_report = lt_report + it_member = lt_member ). + cl_abap_unit_assert=>assert_equals( exp = 'SPAE' act = lt_result[ 1 ]-variety ). + cl_abap_unit_assert=>assert_equals( exp = 'SILV' act = lt_result[ 2 ]-variety ). + cl_abap_unit_assert=>assert_equals( exp = 'RIES' act = lt_result[ 3 ]-variety ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1301/seed/if_harvest.intf.abap b/tasks_gen/train/G1301/seed/if_harvest.intf.abap new file mode 100644 index 0000000..103a82c --- /dev/null +++ b/tasks_gen/train/G1301/seed/if_harvest.intf.abap @@ -0,0 +1,26 @@ +INTERFACE {{p}}if_harvest PUBLIC. + TYPES ty_member_id TYPE c LENGTH 6. + TYPES ty_variety TYPE c LENGTH 4. + TYPES ty_name TYPE c LENGTH 30. + TYPES ty_quantity TYPE i. + + TYPES: BEGIN OF ty_member, + member_id TYPE ty_member_id, + member_name TYPE ty_name, + END OF ty_member. + TYPES tt_member TYPE STANDARD TABLE OF ty_member WITH EMPTY KEY. + + TYPES: BEGIN OF ty_report, + member_id TYPE ty_member_id, + variety TYPE ty_variety, + quantity_kg TYPE ty_quantity, + END OF ty_report. + TYPES tt_report TYPE STANDARD TABLE OF ty_report WITH EMPTY KEY. + + TYPES: BEGIN OF ty_result, + variety TYPE ty_variety, + total_kg TYPE ty_quantity, + member_count TYPE i, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY. +ENDINTERFACE. diff --git a/tasks_gen/train/G1301/spec.md b/tasks_gen/train/G1301/spec.md new file mode 100644 index 0000000..4ffd10d --- /dev/null +++ b/tasks_gen/train/G1301/spec.md @@ -0,0 +1,7 @@ +Create class {{P}}HARVEST_EVALUATOR in package $TMP. Make the class public and final. Add a public constructor without parameters. Add one public method EVALUATE with this signature: IMPORTING it_report TYPE {{P}}IF_HARVEST=>TT_REPORT it_member TYPE {{P}}IF_HARVEST=>TT_MEMBER RETURNING VALUE(rt_result) TYPE {{P}}IF_HARVEST=>TT_RESULT. Do not add other public methods. + +A harvest report is countable when the member_id of the report occurs in it_member and the quantity_kg of the report is greater than 0. The member_id occurs at most one time in it_member. Group the countable reports by variety. Create one result row for each variety that has at least one countable report. The total_kg of a result row is the sum of the quantity_kg of all countable reports of the variety. The member_count of a result row is the number of different member_id values in the countable reports of the variety. Sort the result rows by total_kg in descending order. When two rows have the same total_kg, sort these rows by variety in ascending order. When no report is countable, return an empty table. + +The result table has columns variety, total_kg, and member_count. + +Release target is 8.16. Do not change {{P}}IF_HARVEST. Write ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1301/task.json b/tasks_gen/train/G1301/task.json new file mode 100644 index 0000000..95a0fd4 --- /dev/null +++ b/tasks_gen/train/G1301/task.json @@ -0,0 +1,55 @@ +{ + "id": "G1301", + "category": "K", + "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_HARVEST", + "file": "seed/if_harvest.intf.abap", + "description": "Data types for harvest reports, member master data, and the evaluation result" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_EVALUATOR", + "description": "Evaluates the harvest reports per grape variety" + } + ], + "out_of_scope": [ + "{{P}}IF_HARVEST" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_HIDDEN", + "file": "hidden/harvest_hidden.clas.abap", + "description": "Hidden tests for the harvest evaluation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}HARVEST_EVALUATOR", + "file": "reference/harvest_evaluator.clas.abap", + "description": "Harvest evaluator", + "testclasses_file": "reference/harvest_evaluator.testclasses.abap" + } + ], + "craft_checks": [ + "table_kind", + "key_kind", + "access_path", + "method_length" + ], + "base_task": "G1001", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1302/generation.json b/tasks_gen/train/G1302/generation.json new file mode 100644 index 0000000..dcac226 --- /dev/null +++ b/tasks_gen/train/G1302/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1302", + "pool": "train", + "category": "K", + "base_task": "G1006", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1302/hidden/t14_hidden.clas.abap b/tasks_gen/train/G1302/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..b6c716b --- /dev/null +++ b/tasks_gen/train/G1302/hidden/t14_hidden.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS band_first FOR TESTING. + METHODS band_second FOR TESTING. + METHODS band_third FOR TESTING. + METHODS online_reduction FOR TESTING. + METHODS premium_rounding FOR TESTING. + METHODS minimum_premium FOR TESTING. + METHODS maximum_premium FOR TESTING. + METHODS charity_skips FOR TESTING. + METHODS negative_hammer FOR TESTING. + METHODS skip_false_for_sale FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = iv_lot_type + iv_hammer = iv_hammer + iv_channel = iv_channel + IMPORTING + ev_skip = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD band_first. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 100. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD band_second. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 320. + call( EXPORTING iv_lot_type = `S` iv_hammer = 2000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD band_third. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 1880. + call( EXPORTING iv_lot_type = `S` iv_hammer = 20000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD online_reduction. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 90. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `O` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD premium_rounding. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + DATA lv_hammer TYPE decfloat34. + lv_exp = '228.15'. + lv_hammer = '1234.56'. + call( EXPORTING iv_lot_type = `S` iv_hammer = lv_hammer iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD minimum_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5. + call( EXPORTING iv_lot_type = `S` iv_hammer = 10 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD maximum_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5000. + call( EXPORTING iv_lot_type = `S` iv_hammer = 100000 iv_channel = `B` + IMPORTING ev_fee = lv_act ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD charity_skips. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_fee = 7. + lv_exp = 7. + call( EXPORTING iv_lot_type = `C` iv_hammer = 5000 iv_channel = `B` 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 negative_hammer. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lot_type = `S` iv_hammer = -1 iv_channel = `B` + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD skip_false_for_sale. + DATA lv_skip TYPE abap_bool. + call( EXPORTING iv_lot_type = `S` iv_hammer = 500 iv_channel = `B` + IMPORTING ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1302/reference/auc_premium.func.abap b/tasks_gen/train/G1302/reference/auc_premium.func.abap new file mode 100644 index 0000000..d603665 --- /dev/null +++ b/tasks_gen/train/G1302/reference/auc_premium.func.abap @@ -0,0 +1,41 @@ +FUNCTION {{p}}auc_premium + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + DATA lv_premium TYPE decfloat34. + + ev_skip = abap_false. + IF iv_hammer < 0. + RAISE invalid_hammer. + ENDIF. + IF iv_lot_type = 'C'. + ev_skip = abap_true. + RETURN. + ENDIF. + IF iv_hammer <= 1000. + lv_premium = iv_hammer * 20 / 100. + ELSEIF iv_hammer <= 10000. + lv_premium = 1000 * 20 / 100 + ( iv_hammer - 1000 ) * 12 / 100. + ELSE. + lv_premium = 1000 * 20 / 100 + 9000 * 12 / 100 + ( iv_hammer - 10000 ) * 6 / 100. + ENDIF. + IF iv_channel = 'O'. + lv_premium = lv_premium * 90 / 100. + ENDIF. + lv_premium = round( val = lv_premium dec = 2 mode = cl_abap_math=>round_half_up ). + IF lv_premium < 5. + lv_premium = 5. + ELSEIF lv_premium > 5000. + lv_premium = 5000. + ENDIF. + cv_fee = lv_premium. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1302/reference/t14_test.clas.abap b/tasks_gen/train/G1302/reference/t14_test.clas.abap new file mode 100644 index 0000000..7883737 --- /dev/null +++ b/tasks_gen/train/G1302/reference/t14_test.clas.abap @@ -0,0 +1,99 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS premium_for + IMPORTING iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS band_premium FOR TESTING. + METHODS online_reduction FOR TESTING. + METHODS premium_limits FOR TESTING. + METHODS charity_lot FOR TESTING. + METHODS negative_hammer FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + METHOD premium_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = iv_lot_type + iv_hammer = iv_hammer + iv_channel = iv_channel + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD band_premium. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 320. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 2000 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD online_reduction. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 90. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 500 iv_channel = `O` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD premium_limits. + DATA lv_exp TYPE decfloat34. + DATA lv_act TYPE decfloat34. + lv_exp = 5. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 10 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + lv_exp = 5000. + lv_act = premium_for( iv_lot_type = `S` iv_hammer = 100000 iv_channel = `B` ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_act ). + ENDMETHOD. + + METHOD charity_lot. + DATA lv_skip TYPE abap_bool. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34. + lv_fee = 7. + lv_exp = 7. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = `C` + iv_hammer = 5000 + iv_channel = `B` + IMPORTING + ev_skip = lv_skip + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_true( lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD negative_hammer. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}AUC_PREMIUM' + EXPORTING + iv_lot_type = `S` + iv_hammer = -5 + iv_channel = `B` + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hammer = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1302/seed/auc_sample.func.abap b/tasks_gen/train/G1302/seed/auc_sample.func.abap new file mode 100644 index 0000000..73c393f --- /dev/null +++ b/tasks_gen/train/G1302/seed/auc_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}auc_sample + IMPORTING + iv_lot_type TYPE string + iv_hammer TYPE decfloat34 + iv_channel TYPE string + EXPORTING + ev_skip TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hammer. + + ev_skip = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1302/spec.md b/tasks_gen/train/G1302/spec.md new file mode 100644 index 0000000..bf6f889 --- /dev/null +++ b/tasks_gen/train/G1302/spec.md @@ -0,0 +1,5 @@ +Please create the function module {{P}}AUC_PREMIUM in function group {{P}}FG_AUCT. The function group is in package $TMP. The function module must use exactly the same interface as the sample module {{P}}AUC_SAMPLE: the same parameter names, kinds, types, and exceptions. The parameters are iv_lot_type, iv_hammer, iv_channel, ev_skip, and cv_fee. The exception is INVALID_HAMMER. Do not change {{P}}AUC_SAMPLE. + +The module calculates a buyer's premium for each sold lot. The auction program calls one premium module for each sold lot. If iv_hammer is less than 0, raise the exception INVALID_HAMMER and stop. Set ev_skip to false, except for lot type C. For lot type C (charity), set ev_skip to true. Leave cv_fee unchanged and stop. For every other lot type, calculate the band premium from iv_hammer. The part of iv_hammer up to 1,000.00 has a 20% rate. The part above 1,000.00 up to 10,000.00 has a 12% rate. The part above 10,000.00 has a 6% rate. For channel O (online), the premium is 90% of the band premium. For every other channel, the premium is the band premium. Round the premium half up to 2 decimals. The minimum premium is 5.00. The maximum premium is 5,000.00. Set cv_fee to the premium after rounding and applying the minimum and maximum. + +The code must run on a 7.02 system. Do not use syntax that was introduced after release 7.02. Use Clean ABAP. The function module must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1302/task.json b/tasks_gen/train/G1302/task.json new file mode 100644 index 0000000..942307e --- /dev/null +++ b/tasks_gen/train/G1302/task.json @@ -0,0 +1,86 @@ +{ + "id": "G1302", + "category": "K", + "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_AUCT", + "description": "Buyer's premium modules" + }, + { + "type": "FUNC", + "name": "{{P}}AUC_SAMPLE", + "functionGroup": "{{P}}FG_AUCT", + "file": "seed/auc_sample.func.abap", + "description": "Sample premium module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}AUC_PREMIUM", + "functionGroup": "{{P}}FG_AUCT", + "params": [ + { + "name": "iv_lot_type", + "type": "string" + }, + { + "name": "iv_hammer", + "type": "decfloat34" + }, + { + "name": "iv_channel", + "type": "string" + }, + { + "name": "ev_skip", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}AUC_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}AUC_PREMIUM", + "functionGroup": "{{P}}FG_AUCT", + "file": "reference/auc_premium.func.abap", + "description": "Buyer's premium module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "no_new_syntax" + ], + "base_task": "G1006", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1303/generation.json b/tasks_gen/train/G1303/generation.json new file mode 100644 index 0000000..a07ec49 --- /dev/null +++ b/tasks_gen/train/G1303/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1303", + "pool": "train", + "category": "K", + "base_task": "G1002", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1303/hidden/glasses_hidden.clas.abap b/tasks_gen/train/G1303/hidden/glasses_hidden.clas.abap new file mode 100644 index 0000000..757c44e --- /dev/null +++ b/tasks_gen/train/G1303/hidden/glasses_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}glasses_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + CONSTANTS c_frame TYPE {{p}}glasses_pricer=>ty_amount VALUE '100.00'. + DATA mo_cut TYPE REF TO {{p}}glasses_pricer. + + METHODS setup. + METHODS zero_qty_is_free FOR TESTING. + METHODS negative_qty_is_free FOR TESTING. + METHODS single_vision_price FOR TESTING. + METHODS bifocal_with_coating FOR TESTING. + METHODS progressive_blue_filter FOR TESTING. + METHODS unknown_lens_defaults FOR TESTING. + METHODS unknown_coat_is_free FOR TESTING. + METHODS discount_from_three FOR TESTING. + METHODS no_discount_for_two FOR TESTING. + METHODS qty_capped_at_hundred FOR TESTING. + METHODS discount_on_total FOR TESTING. +ENDCLASS. + + +CLASS {{p}}glasses_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}glasses_pricer( ). + ENDMETHOD. + + METHOD zero_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'A' iv_qty = 0 ) ). + ENDMETHOD. + + METHOD negative_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'P' iv_coat = 'B' iv_qty = -2 ) ). + ENDMETHOD. + + METHOD single_vision_price. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD bifocal_with_coating. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '430.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'B' iv_coat = 'A' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD progressive_blue_filter. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '235.00' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '50.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_lens_defaults. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'Z' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_coat_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'X' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD discount_from_three. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '378.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 3 ) ). + ENDMETHOD. + + METHOD no_discount_for_two. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '280.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD qty_capped_at_hundred. + DATA(lv_capped) = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 100 ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '12600.00' ) + act = lv_capped ). + cl_abap_unit_assert=>assert_equals( + exp = lv_capped + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 150 ) ). + ENDMETHOD. + + METHOD discount_on_total. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '499.50' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 3 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1303/reference/glasses_pricer.clas.abap b/tasks_gen/train/G1303/reference/glasses_pricer.clas.abap new file mode 100644 index 0000000..84d5a08 --- /dev/null +++ b/tasks_gen/train/G1303/reference/glasses_pricer.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + TYPES ty_lens TYPE c LENGTH 1. + TYPES ty_coat TYPE c LENGTH 1. + METHODS price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_max_qty TYPE i VALUE 100, + c_discount_qty TYPE i VALUE 3, + c_discount_percent TYPE i VALUE 10, + c_lens_standard TYPE ty_amount VALUE '40.00', + c_lens_bifocal TYPE ty_amount VALUE '90.00', + c_lens_progressive TYPE ty_amount VALUE '150.00', + c_coat_simple TYPE ty_amount VALUE '25.00', + c_coat_premium TYPE ty_amount VALUE '35.00', + c_coat_none TYPE ty_amount VALUE '0.00'. + + METHODS capped_qty + IMPORTING iv_qty TYPE i + RETURNING VALUE(rv_qty) TYPE i. + METHODS lens_surcharge + IMPORTING iv_lens TYPE ty_lens + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS coat_surcharge + IMPORTING iv_coat TYPE ty_coat + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS total_price + IMPORTING iv_frame TYPE ty_amount + iv_lens TYPE ty_amount + iv_coat TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_total) TYPE ty_amount. + METHODS with_discount + IMPORTING iv_total TYPE ty_amount + iv_qty TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + DATA(lv_qty) = capped_qty( iv_qty ). + IF lv_qty < 1. + RETURN. + ENDIF. + + DATA(lv_total) = total_price( iv_frame = iv_frame + iv_lens = lens_surcharge( iv_lens ) + iv_coat = coat_surcharge( iv_coat ) + iv_qty = lv_qty ). + + rv_price = with_discount( iv_total = lv_total + iv_qty = lv_qty ). + ENDMETHOD. + + METHOD capped_qty. + rv_qty = iv_qty. + IF rv_qty > c_max_qty. + rv_qty = c_max_qty. + ENDIF. + ENDMETHOD. + + METHOD lens_surcharge. + CASE iv_lens. + WHEN 'B'. + rv_amount = c_lens_bifocal. + WHEN 'P'. + rv_amount = c_lens_progressive. + WHEN OTHERS. + rv_amount = c_lens_standard. + ENDCASE. + ENDMETHOD. + + METHOD coat_surcharge. + CASE iv_coat. + WHEN 'A'. + rv_amount = c_coat_simple. + WHEN 'B'. + rv_amount = c_coat_premium. + WHEN OTHERS. + rv_amount = c_coat_none. + ENDCASE. + ENDMETHOD. + + METHOD total_price. + rv_total = iv_frame * iv_qty. + rv_total = rv_total + iv_lens * iv_qty. + rv_total = rv_total + iv_coat * iv_qty. + ENDMETHOD. + + METHOD with_discount. + rv_price = iv_total. + IF iv_qty >= c_discount_qty. + rv_price = iv_total - iv_total * c_discount_percent / 100. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1303/reference/glasses_pricer.testclasses.abap b/tasks_gen/train/G1303/reference/glasses_pricer.testclasses.abap new file mode 100644 index 0000000..ecffa90 --- /dev/null +++ b/tasks_gen/train/G1303/reference/glasses_pricer.testclasses.abap @@ -0,0 +1,83 @@ +CLASS ltc_glasses_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS c_frame TYPE {{p}}glasses_pricer=>ty_amount VALUE '100.00'. + DATA mo_cut TYPE REF TO {{p}}glasses_pricer. + + METHODS setup. + METHODS single_vision FOR TESTING. + METHODS bifocal_with_coating FOR TESTING. + METHODS progressive_with_blue FOR TESTING. + METHODS unknown_lens_defaults FOR TESTING. + METHODS unknown_coat_is_free FOR TESTING. + METHODS discount_from_three FOR TESTING. + METHODS no_discount_for_two FOR TESTING. + METHODS zero_qty_is_free FOR TESTING. + METHODS qty_capped_at_hundred FOR TESTING. +ENDCLASS. + + +CLASS ltc_glasses_pricer IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW #( ). + ENDMETHOD. + + METHOD single_vision. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD bifocal_with_coating. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '430.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'B' iv_coat = 'A' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD progressive_with_blue. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '235.00' ) + act = mo_cut->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '50.00' ) + iv_lens = 'P' iv_coat = 'B' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_lens_defaults. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'Z' iv_coat = 'N' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD unknown_coat_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '140.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'X' iv_qty = 1 ) ). + ENDMETHOD. + + METHOD discount_from_three. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '378.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 3 ) ). + ENDMETHOD. + + METHOD no_discount_for_two. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '280.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 2 ) ). + ENDMETHOD. + + METHOD zero_qty_is_free. + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '0.00' ) + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'A' iv_qty = 0 ) ). + ENDMETHOD. + + METHOD qty_capped_at_hundred. + DATA(lv_capped) = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 100 ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}glasses_pricer=>ty_amount( '12600.00' ) + act = lv_capped ). + cl_abap_unit_assert=>assert_equals( + exp = lv_capped + act = mo_cut->price( iv_frame = c_frame iv_lens = 'S' iv_coat = 'N' iv_qty = 150 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1303/seed/glasses_pricer.clas.abap b/tasks_gen/train/G1303/seed/glasses_pricer.clas.abap new file mode 100644 index 0000000..6c2f86c --- /dev/null +++ b/tasks_gen/train/G1303/seed/glasses_pricer.clas.abap @@ -0,0 +1,75 @@ +CLASS {{p}}glasses_pricer DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2 . + TYPES ty_lens TYPE c LENGTH 1 . + TYPES ty_coat TYPE c LENGTH 1 . + METHODS price + IMPORTING + iv_frame TYPE ty_amount + iv_lens TYPE ty_lens + iv_coat TYPE ty_coat + iv_qty TYPE i + RETURNING + VALUE(rv_price) TYPE ty_amount . + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}glasses_pricer IMPLEMENTATION. + + METHOD price. + + DATA lv_qty TYPE i . + DATA lv_frame TYPE ty_amount . + DATA lv_lens TYPE ty_amount . + DATA lv_coat TYPE ty_amount . + DATA lv_total TYPE ty_amount . + + lv_qty = iv_qty . + IF lv_qty > 100 . + lv_qty = 100 . + ENDIF . + + IF lv_qty < 1 . + rv_price = 0 . + RETURN . + ENDIF . + + lv_frame = iv_frame * lv_qty . + + IF iv_lens = 'S' . + lv_lens = 40 . + ELSEIF iv_lens = 'B' . + lv_lens = 90 . + ELSEIF iv_lens = 'P' . + lv_lens = 150 . + ELSE . + lv_lens = 40 . + ENDIF . + lv_lens = lv_lens * lv_qty . + + CASE iv_coat . + WHEN 'A' . + lv_coat = 25 . + WHEN 'B' . + lv_coat = 35 . + WHEN OTHERS . + lv_coat = 0 . + ENDCASE . + lv_coat = lv_coat * lv_qty . + + lv_total = lv_frame + lv_lens + lv_coat . + + IF lv_qty >= 3 . + lv_total = lv_total - lv_total * 10 / 100 . + ENDIF . + + rv_price = lv_total . + + ENDMETHOD . +ENDCLASS. diff --git a/tasks_gen/train/G1303/seed/optic_quote_rpt.prog.abap b/tasks_gen/train/G1303/seed/optic_quote_rpt.prog.abap new file mode 100644 index 0000000..d3d4786 --- /dev/null +++ b/tasks_gen/train/G1303/seed/optic_quote_rpt.prog.abap @@ -0,0 +1,12 @@ +REPORT {{p}}optic_quote_rpt. + +DATA lo_pricer TYPE REF TO {{p}}glasses_pricer. +DATA lv_price TYPE {{p}}glasses_pricer=>ty_amount. + +lo_pricer = NEW #( ). +lv_price = lo_pricer->price( iv_frame = CONV {{p}}glasses_pricer=>ty_amount( '120.00' ) + iv_lens = 'P' + iv_coat = 'A' + iv_qty = 2 ). + +WRITE: / 'Quotation:', lv_price. diff --git a/tasks_gen/train/G1303/spec.md b/tasks_gen/train/G1303/spec.md new file mode 100644 index 0000000..486c87e --- /dev/null +++ b/tasks_gen/train/G1303/spec.md @@ -0,0 +1,9 @@ +Refactor the class {{P}}GLASSES_PRICER. The class name stays the same. The behavior must stay the same. The legacy implementation is the specification of the behavior. For every input, the method PRICE must return the same value as the legacy method. Do not add a rule. Do not remove a rule. Do not correct a rule. PRICE returns a price in the currency of the order. + +The class stays public and final. It keeps its public constructor without parameters. Keep the public types TY_AMOUNT, TY_LENS, and TY_COAT. Keep the method PRICE with the same parameter names, parameter types, and sequence: importing IV_FRAME (TY_AMOUNT), IV_LENS (TY_LENS), IV_COAT (TY_COAT), IV_QTY (TYPE i); returning RV_PRICE (TY_AMOUNT). Do not add public methods or public attributes. Do not create other objects. + +Do not change the program {{P}}OPTIC_QUOTE_RPT. Do not change the public interface of the class. + +Release target: ABAP Platform 2025 (SAP_BASIS 816). Use Clean ABAP. One method does one thing. Method length below 40 statements. No deep nesting. No magic numbers: use constants. No duplicated code. No dead code. No global variables. No comment that restates the code. + +The class must be active and have no syntax error. The hidden tests must pass. Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1303/task.json b/tasks_gen/train/G1303/task.json new file mode 100644 index 0000000..64baac7 --- /dev/null +++ b/tasks_gen/train/G1303/task.json @@ -0,0 +1,59 @@ +{ + "id": "G1303", + "category": "K", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 18 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER", + "file": "seed/glasses_pricer.clas.abap", + "description": "Legacy price calculator for glasses orders" + }, + { + "type": "PROG", + "name": "{{P}}OPTIC_QUOTE_RPT", + "file": "seed/optic_quote_rpt.prog.abap", + "description": "Report that calls the legacy class" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER" + } + ], + "out_of_scope": [ + "{{P}}OPTIC_QUOTE_RPT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_HIDDEN", + "file": "hidden/glasses_hidden.clas.abap", + "description": "Hidden behavior tests for the refactored class" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}GLASSES_PRICER", + "file": "reference/glasses_pricer.clas.abap", + "description": "Refactored glasses price calculator", + "testclasses_file": "reference/glasses_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nesting_depth", + "magic_numbers" + ], + "base_task": "G1002", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1304/generation.json b/tasks_gen/train/G1304/generation.json new file mode 100644 index 0000000..1a67cac --- /dev/null +++ b/tasks_gen/train/G1304/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1304", + "pool": "train", + "category": "K", + "base_task": "G1011", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1304/hidden/repair_charge_hidden.clas.abap b/tasks_gen/train/G1304/hidden/repair_charge_hidden.clas.abap new file mode 100644 index 0000000..c743943 --- /dev/null +++ b/tasks_gen/train/G1304/hidden/repair_charge_hidden.clas.abap @@ -0,0 +1,97 @@ +CLASS {{p}}repair_charge_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 charge_c1 FOR TESTING. + METHODS charge_c2 FOR TESTING. + METHODS count_c1 FOR TESTING. + METHODS count_c2 FOR TESTING. + METHODS pairs_c1 FOR TESTING. + METHODS pairs_c2 FOR TESTING. + METHODS single_rate_ok FOR TESTING. + METHODS open_job_hidden FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}repair_charge_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_REPAIR_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_job = VALUE #( + ( job_id = 'J1' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'J2' customer = 'C1' shoe_type = 'SHOE' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J3' customer = 'C2' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 3 job_state = 'D' ) + ( job_id = 'J4' customer = 'C3' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 1 job_state = 'O' ) + ( job_id = 'J5' customer = 'C2' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J6' customer = 'C4' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 2 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '12.50' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '6.25' ) + ( shoe_type = 'SHOE' repair_type = 'HEEL' price = '4.00' ) + ( shoe_type = 'SHOE' repair_type = 'SOLE' price = '3.75' ) + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.00' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + ENDMETHOD. + + METHOD charge_c1. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '29.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_c2. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '43.75' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD count_c1. + SELECT SINGLE job_count FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD count_c2. + SELECT SINGLE job_count FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD pairs_c1. + SELECT SINGLE total_pairs FROM {{p}}i_repair_charge WHERE customer = 'C1' INTO @DATA(lv_pairs). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_pairs ). + ENDMETHOD. + + METHOD pairs_c2. + SELECT SINGLE total_pairs FROM {{p}}i_repair_charge WHERE customer = 'C2' INTO @DATA(lv_pairs). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lv_pairs ). + ENDMETHOD. + + METHOD single_rate_ok. + SELECT SINGLE total_charge FROM {{p}}i_repair_charge WHERE customer = 'C4' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD open_job_hidden. + SELECT COUNT(*) FROM {{p}}i_repair_charge WHERE customer = 'C3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_repair_charge INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1304/reference/i_repair_charge.ddls.asddls b/tasks_gen/train/G1304/reference/i_repair_charge.ddls.asddls new file mode 100644 index 0000000..05fd992 --- /dev/null +++ b/tasks_gen/train/G1304/reference/i_repair_charge.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type + and rate.repair_type = job.repair_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1304/reference/repair_charge_test.clas.abap b/tasks_gen/train/G1304/reference/repair_charge_test.clas.abap new file mode 100644 index 0000000..dfd4146 --- /dev/null +++ b/tasks_gen/train/G1304/reference/repair_charge_test.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}repair_charge_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 charge_of_two_jobs FOR TESTING. + METHODS charge_of_single_rate FOR TESTING. +ENDCLASS. + + +CLASS {{p}}repair_charge_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_REPAIR_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD charge_of_two_jobs. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + lt_job = VALUE #( + ( job_id = 'A' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'B' customer = 'C1' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '10.00' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '5.00' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + SELECT SINGLE job_count, total_pairs, total_charge FROM {{p}}i_repair_charge + WHERE customer = 'C1' INTO @DATA(ls_charge). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_charge-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_charge-total_pairs ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.00' ) + act = CONV decfloat34( ls_charge-total_charge ) ). + ENDMETHOD. + + METHOD charge_of_single_rate. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + lt_job = VALUE #( + ( job_id = 'C' customer = 'C2' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 4 job_state = 'D' ) ). + lt_rate = VALUE #( + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.50' ) ). + go_env->insert_test_data( lt_job ). + go_env->insert_test_data( lt_rate ). + SELECT SINGLE total_charge FROM {{p}}i_repair_charge + WHERE customer = 'C2' INTO @DATA(lv_charge). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '22.00' ) + act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1304/seed/i_repair_charge.ddls.asddls b/tasks_gen/train/G1304/seed/i_repair_charge.ddls.asddls new file mode 100644 index 0000000..88b1d59 --- /dev/null +++ b/tasks_gen/train/G1304/seed/i_repair_charge.ddls.asddls @@ -0,0 +1,14 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Repair charge per customer' +define view entity {{P}}I_REPAIR_CHARGE + as select from {{p}}job as job + inner join {{p}}rate as rate on rate.shoe_type = job.shoe_type +{ + key job.customer as customer, + count( * ) as job_count, + sum( job.pairs ) as total_pairs, + sum( cast( job.pairs as abap.dec( 10, 0 ) ) * rate.price ) as total_charge +} +where job.job_state = 'D' +group by + job.customer diff --git a/tasks_gen/train/G1304/seed/job.tabl.asabap b/tasks_gen/train/G1304/seed/job.tabl.asabap new file mode 100644 index 0000000..88f9725 --- /dev/null +++ b/tasks_gen/train/G1304/seed/job.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Repair job' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}job { + key client : abap.clnt not null; + key job_id : abap.char(10) not null; + customer : abap.char(10); + shoe_type : abap.char(4); + repair_type : abap.char(4); + pairs : abap.int4; + job_state : abap.char(1); +} diff --git a/tasks_gen/train/G1304/seed/rate.tabl.asabap b/tasks_gen/train/G1304/seed/rate.tabl.asabap new file mode 100644 index 0000000..a4cc904 --- /dev/null +++ b/tasks_gen/train/G1304/seed/rate.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Repair rate' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}rate { + key client : abap.clnt not null; + key shoe_type : abap.char(4) not null; + key repair_type : abap.char(4) not null; + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1304/seed/seed_repair.clas.abap b/tasks_gen/train/G1304/seed/seed_repair.clas.abap new file mode 100644 index 0000000..bb290a3 --- /dev/null +++ b/tasks_gen/train/G1304/seed/seed_repair.clas.abap @@ -0,0 +1,34 @@ +CLASS {{p}}seed_repair DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_repair IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_job TYPE STANDARD TABLE OF {{p}}job WITH EMPTY KEY. + DATA lt_rate TYPE STANDARD TABLE OF {{p}}rate WITH EMPTY KEY. + + lt_job = VALUE #( + ( job_id = 'J1' customer = 'C1' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 2 job_state = 'D' ) + ( job_id = 'J2' customer = 'C1' shoe_type = 'SHOE' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J3' customer = 'C2' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 3 job_state = 'D' ) + ( job_id = 'J4' customer = 'C3' shoe_type = 'BOOT' repair_type = 'SOLE' pairs = 1 job_state = 'O' ) + ( job_id = 'J5' customer = 'C2' shoe_type = 'BOOT' repair_type = 'HEEL' pairs = 1 job_state = 'D' ) + ( job_id = 'J6' customer = 'C4' shoe_type = 'SANDAL' repair_type = 'STRAP' pairs = 2 job_state = 'D' ) ). + + lt_rate = VALUE #( + ( shoe_type = 'BOOT' repair_type = 'SOLE' price = '12.50' ) + ( shoe_type = 'BOOT' repair_type = 'HEEL' price = '6.25' ) + ( shoe_type = 'SHOE' repair_type = 'HEEL' price = '4.00' ) + ( shoe_type = 'SHOE' repair_type = 'SOLE' price = '3.75' ) + ( shoe_type = 'SANDAL' repair_type = 'STRAP' price = '5.00' ) ). + + DELETE FROM {{p}}job. + DELETE FROM {{p}}rate. + INSERT {{p}}job FROM TABLE @lt_job. + INSERT {{p}}rate FROM TABLE @lt_rate. + + out->write( |Jobs: { lines( lt_job ) }, rates: { lines( lt_rate ) }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1304/spec.md b/tasks_gen/train/G1304/spec.md new file mode 100644 index 0000000..159eb2a --- /dev/null +++ b/tasks_gen/train/G1304/spec.md @@ -0,0 +1 @@ +Hi, please fix the CDS view entity {{P}}I_REPAIR_CHARGE in package $TMP. The view gives the total repair charge of each customer of a cobbler's workshop. A report and an OData service read the charges. The calculation must be in the database, in a CDS view. The view entity already exists and has a defect. The elements are CUSTOMER as key, JOB_COUNT, TOTAL_PAIRS, and TOTAL_CHARGE. The authorization check is #NOT_REQUIRED. The view shows one row for each customer that has at least one repair job in the view. JOB_COUNT is the number of repair jobs of the customer in the view. TOTAL_PAIRS is the sum of PAIRS of these jobs. The charge of a repair job is its PAIRS multiplied by the price that the rate table holds for the shoe type and the repair type of the job. TOTAL_CHARGE is the sum of the charges of these jobs, with 2 decimals. Users report that JOB_COUNT, TOTAL_PAIRS, and TOTAL_CHARGE of a customer are too high. The three values are correct when the customer has no job whose shoe type has more than one repair type in the rate table. The three values must be correct for every customer. All other behavior of the view stays the same. Use table {{P}}JOB for repair jobs. Its key is JOB_ID (CHAR 10). Its fields are CUSTOMER (CHAR 10), SHOE_TYPE (CHAR 4), REPAIR_TYPE (CHAR 4), PAIRS (INT4), and JOB_STATE (CHAR 1). Use table {{P}}RATE for repair rates. Its key is SHOE_TYPE (CHAR 4) and REPAIR_TYPE (CHAR 4). Its field is PRICE (DEC 15,2, price for one pair). Both tables are in package $TMP. Do not change {{P}}JOB and {{P}}RATE. The release target is 8.16. Follow Clean ABAP. There must be no ATC priority 1 or 2 findings. No dynpro, no SmartForms, no BAdI, and no RAP behavior definition. The view must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. There are no open questions. diff --git a/tasks_gen/train/G1304/task.json b/tasks_gen/train/G1304/task.json new file mode 100644 index 0000000..2338cbb --- /dev/null +++ b/tasks_gen/train/G1304/task.json @@ -0,0 +1,80 @@ +{ + "id": "G1304", + "category": "K", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}JOB", + "file": "seed/job.tabl.asabap", + "description": "Repair job" + }, + { + "type": "TABL", + "name": "{{P}}RATE", + "file": "seed/rate.tabl.asabap", + "description": "Repair rate" + }, + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "file": "seed/i_repair_charge.ddls.asddls", + "description": "Faulty repair charge view" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_REPAIR", + "file": "seed/seed_repair.clas.abap", + "description": "Seed data for the repair tables", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "fields": [ + "CUSTOMER", + "JOB_COUNT", + "TOTAL_PAIRS", + "TOTAL_CHARGE" + ] + } + ], + "out_of_scope": [ + "{{P}}JOB", + "{{P}}RATE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}REPAIR_CHARGE_HIDDEN", + "file": "hidden/repair_charge_hidden.clas.abap", + "description": "Hidden tests for the repair charge view" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_REPAIR_CHARGE", + "file": "reference/i_repair_charge.ddls.asddls", + "description": "Fixed repair charge view" + }, + { + "type": "CLAS", + "name": "{{P}}REPAIR_CHARGE_TEST", + "file": "reference/repair_charge_test.clas.abap", + "description": "Own tests for the repair charge view" + } + ], + "craft_checks": [], + "base_task": "G1011", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1305/generation.json b/tasks_gen/train/G1305/generation.json new file mode 100644 index 0000000..59177bb --- /dev/null +++ b/tasks_gen/train/G1305/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1305", + "pool": "train", + "category": "K", + "base_task": "G1018", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1305/hidden/ad_hidden.clas.abap b/tasks_gen/train/G1305/hidden/ad_hidden.clas.abap new file mode 100644 index 0000000..3a98964 --- /dev/null +++ b/tasks_gen/train/G1305/hidden/ad_hidden.clas.abap @@ -0,0 +1,125 @@ +CLASS {{p}}ad_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS lc_price_2490 TYPE decfloat34 VALUE '24.90'. + CONSTANTS lc_price_4990 TYPE decfloat34 VALUE '49.90'. + CONSTANTS lc_price_2000 TYPE decfloat34 VALUE '20.00'. + CONSTANTS lc_price_4482 TYPE decfloat34 VALUE '44.82'. + CONSTANTS lc_price_3984 TYPE decfloat34 VALUE '39.84'. + CONSTANTS lc_price_3362 TYPE decfloat34 VALUE '33.62'. + CONSTANTS lc_price_6980 TYPE decfloat34 VALUE '69.80'. + METHODS call + IMPORTING iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool DEFAULT abap_false + EXPORTING ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS assert_price + IMPORTING iv_expected TYPE decfloat34 + iv_actual TYPE decfloat34. + METHODS two_lines_one_run FOR TESTING. + METHODS photo_surcharge FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS minimum_after_disc FOR TESTING. + METHODS discount_ten_percent FOR TESTING. + METHODS discount_twenty_pct FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS photo_with_discount FOR TESTING. + METHODS rejects_zero_lines FOR TESTING. + METHODS rejects_zero_runs FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ad_hidden IMPLEMENTATION. + METHOD call. + ev_price = 0. + CALL FUNCTION '{{P}}AD_PRICE' + EXPORTING + iv_lines = iv_lines + iv_insertions = iv_insertions + iv_photo = iv_photo + IMPORTING + ev_price = ev_price + EXCEPTIONS + invalid_lines = 1 + invalid_insertions = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD assert_price. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD two_lines_one_run. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + assert_price( iv_expected = lc_price_2490 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_surcharge. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 2 iv_insertions = 1 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4990 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_charge. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 1 iv_insertions = 1 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_after_disc. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 2 iv_insertions = 6 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_ten_percent. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4482 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_twenty_pct. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 6 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_3984 iv_actual = lv_price ). + ENDMETHOD. + + METHOD rounding_half_up. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 3 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_3362 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_with_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 5 iv_insertions = 6 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_6980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD rejects_zero_lines. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 0 iv_insertions = 1 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD rejects_zero_runs. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 0 + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1305/reference/ad_price.func.abap b/tasks_gen/train/G1305/reference/ad_price.func.abap new file mode 100644 index 0000000..7b2eb72 --- /dev/null +++ b/tasks_gen/train/G1305/reference/ad_price.func.abap @@ -0,0 +1,48 @@ +FUNCTION {{p}}ad_price + IMPORTING + iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool + EXPORTING + ev_price TYPE decfloat34 + EXCEPTIONS + invalid_lines + invalid_insertions. + + CONSTANTS lc_line_rate TYPE decfloat34 VALUE '12.45'. + CONSTANTS lc_photo_fee TYPE decfloat34 VALUE '25.00'. + CONSTANTS lc_min_price TYPE decfloat34 VALUE '20.00'. + + DATA lv_price TYPE decfloat34. + DATA lv_discount TYPE decfloat34. + + IF iv_lines < 1. + RAISE invalid_lines. + ENDIF. + + IF iv_insertions < 1. + RAISE invalid_insertions. + ENDIF. + + lv_price = iv_lines * lc_line_rate. + IF iv_photo = abap_true. + lv_price = lv_price + lc_photo_fee. + ENDIF. + + lv_discount = 0. + IF iv_insertions >= 6. + lv_discount = 20. + ELSEIF iv_insertions >= 3. + lv_discount = 10. + ENDIF. + + lv_price = lv_price * ( 100 - lv_discount ) / 100. + lv_price = round( val = lv_price dec = 2 ). + + IF lv_price < lc_min_price. + lv_price = lc_min_price. + ENDIF. + + ev_price = lv_price. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1305/reference/ad_test.clas.abap b/tasks_gen/train/G1305/reference/ad_test.clas.abap new file mode 100644 index 0000000..c74e444 --- /dev/null +++ b/tasks_gen/train/G1305/reference/ad_test.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ad_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS lc_price_2490 TYPE decfloat34 VALUE '24.90'. + CONSTANTS lc_price_4980 TYPE decfloat34 VALUE '49.80'. + CONSTANTS lc_price_4482 TYPE decfloat34 VALUE '44.82'. + CONSTANTS lc_price_6980 TYPE decfloat34 VALUE '69.80'. + CONSTANTS lc_price_2000 TYPE decfloat34 VALUE '20.00'. + METHODS call + IMPORTING iv_lines TYPE i + iv_insertions TYPE i + iv_photo TYPE abap_bool DEFAULT abap_false + EXPORTING ev_price TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS assert_price + IMPORTING iv_expected TYPE decfloat34 + iv_actual TYPE decfloat34. + METHODS line_price FOR TESTING. + METHODS no_discount_short FOR TESTING. + METHODS discount_ten_pct FOR TESTING. + METHODS photo_and_discount FOR TESTING. + METHODS minimum_price FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}ad_test IMPLEMENTATION. + METHOD call. + ev_price = 0. + CALL FUNCTION '{{P}}AD_PRICE' + EXPORTING + iv_lines = iv_lines + iv_insertions = iv_insertions + iv_photo = iv_photo + IMPORTING + ev_price = ev_price + EXCEPTIONS + invalid_lines = 1 + invalid_insertions = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD assert_price. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD line_price. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 2 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + assert_price( iv_expected = lc_price_2490 iv_actual = lv_price ). + ENDMETHOD. + + METHOD no_discount_short. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 2 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD discount_ten_pct. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 4 iv_insertions = 3 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_4482 iv_actual = lv_price ). + ENDMETHOD. + + METHOD photo_and_discount. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 5 iv_insertions = 6 iv_photo = abap_true + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_6980 iv_actual = lv_price ). + ENDMETHOD. + + METHOD minimum_price. + DATA lv_price TYPE decfloat34. + call( EXPORTING iv_lines = 1 iv_insertions = 1 + IMPORTING ev_price = lv_price ). + assert_price( iv_expected = lc_price_2000 iv_actual = lv_price ). + ENDMETHOD. + + METHOD invalid_input. + DATA lv_price TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_lines = 0 iv_insertions = 1 + IMPORTING ev_price = lv_price ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1305/spec.md b/tasks_gen/train/G1305/spec.md new file mode 100644 index 0000000..f3343b2 --- /dev/null +++ b/tasks_gen/train/G1305/spec.md @@ -0,0 +1 @@ +Create function module {{P}}AD_PRICE in function group {{P}}FG_AD. The sales desk calls it for each advertisement order before saving. The function module calculates the price of a classified advertisement for a newspaper. The interface is: importing iv_lines type i for the number of lines; importing iv_insertions type i for the number of insertions; importing iv_photo type abap_bool, which is abap_true if the advertisement has a photo; exporting ev_price type decfloat34 for the price in euro; exceptions invalid_lines and invalid_insertions. If iv_lines is less than 1, raise invalid_lines. If iv_insertions is less than 1, raise invalid_insertions. One line costs 12.45 euro. A photo costs 25.00 euro once for each order. The discount applies to the sum of the line price and the photo price. For 1 or 2 insertions, there is no discount. For 3, 4, or 5 insertions, the discount is 10%. For 6 or more insertions, the discount is 20%. Round the price half up to 2 decimals. The minimum price is 20.00 euro. Apply the minimum price after the discount and after the rounding. Use only ABAP syntax that release 7.40 SP05 supports. Do not use inline declarations or constructor expressions. Follow Clean ABAP. Keep each method below 40 statements. Out of scope: database tables, reports, ALV lists, and other function modules. The function module must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1305/task.json b/tasks_gen/train/G1305/task.json new file mode 100644 index 0000000..2c65109 --- /dev/null +++ b/tasks_gen/train/G1305/task.json @@ -0,0 +1,78 @@ +{ + "id": "G1305", + "category": "K", + "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_AD", + "description": "Function group for advertisement pricing" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}AD_PRICE", + "functionGroup": "{{P}}FG_AD", + "params": [ + { + "name": "iv_lines", + "type": "i" + }, + { + "name": "iv_insertions", + "type": "i" + }, + { + "name": "iv_photo", + "type": "abap_bool" + }, + { + "name": "ev_price", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "database tables", + "reports", + "ALV lists", + "other function modules" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}AD_HIDDEN", + "file": "hidden/ad_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}AD_PRICE", + "functionGroup": "{{P}}FG_AD", + "file": "reference/ad_price.func.abap", + "description": "Advertisement price function module" + }, + { + "type": "CLAS", + "name": "{{P}}AD_TEST", + "file": "reference/ad_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ], + "base_task": "G1018", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1306/generation.json b/tasks_gen/train/G1306/generation.json new file mode 100644 index 0000000..10d2183 --- /dev/null +++ b/tasks_gen/train/G1306/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1306", + "pool": "train", + "category": "K", + "base_task": "G1003", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1306/hidden/t21_hidden.clas.abap b/tasks_gen/train/G1306/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..3429d97 --- /dev/null +++ b/tasks_gen/train/G1306/hidden/t21_hidden.clas.abap @@ -0,0 +1,172 @@ +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_drone_check. + DATA mo_drone TYPE REF TO {{p}}drone_checker. + + METHODS setup. + + METHODS ok_flight FOR TESTING. + METHODS payload_zero FOR TESTING. + METHODS payload_too_heavy FOR TESTING. + METHODS distance_zero FOR TESTING. + METHODS distance_too_far FOR TESTING. + METHODS wind_too_strong FOR TESTING. + METHODS battery_too_low FOR TESTING. + METHODS battery_exact_ok FOR TESTING. + METHODS outside_zone FOR TESTING. + METHODS no_daylight FOR TESTING. + METHODS required_battery_rounds_up FOR TESTING. + METHODS order_payload_first FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD setup. + mo_drone = NEW {{p}}drone_checker( ). + mo_cut = mo_drone. + ENDMETHOD. + + METHOD ok_flight. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '5.00' + iv_distance = '20.00' + iv_wind_speed = '12.00' + iv_battery = 50 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD payload_zero. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '0.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD payload_too_heavy. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '5.01' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD distance_zero. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '0.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD distance_too_far. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '20.01' + iv_wind_speed = '5.00' + iv_battery = 60 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD wind_too_strong. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-wind + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '12.01' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD battery_too_low. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 29 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '7.50' + iv_wind_speed = '5.00' + iv_battery = 25 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD battery_exact_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD outside_zone. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-zone + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_false + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD no_daylight. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-light + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_false ) ). + ENDMETHOD. + + METHOD required_battery_rounds_up. + cl_abap_unit_assert=>assert_equals( exp = 26 act = mo_drone->required_battery( '7.50' ) ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = mo_drone->required_battery( '20.00' ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_drone->required_battery( '0.00' ) ). + ENDMETHOD. + + METHOD order_payload_first. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '6.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1306/reference/drone_checker.clas.abap b/tasks_gen/train/G1306/reference/drone_checker.clas.abap new file mode 100644 index 0000000..9028885 --- /dev/null +++ b/tasks_gen/train/G1306/reference/drone_checker.clas.abap @@ -0,0 +1,49 @@ +CLASS {{p}}drone_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_drone_check. + + METHODS required_battery + IMPORTING iv_distance TYPE {{p}}if_drone_check=>ty_distance + RETURNING VALUE(rv_percent) TYPE {{p}}if_drone_check=>ty_percent. + + PRIVATE SECTION. + CONSTANTS: + c_max_payload TYPE {{p}}if_drone_check=>ty_weight VALUE '5.00', + c_max_distance TYPE {{p}}if_drone_check=>ty_distance VALUE '20.00', + c_max_wind TYPE {{p}}if_drone_check=>ty_speed VALUE '12.00', + c_reserve TYPE {{p}}if_drone_check=>ty_percent VALUE 10, + c_percent_per_km TYPE {{p}}if_drone_check=>ty_percent VALUE 2. +ENDCLASS. + + +CLASS {{p}}drone_checker IMPLEMENTATION. + METHOD {{p}}if_drone_check~check. + IF iv_payload <= 0 OR iv_payload > c_max_payload. + rv_reason = {{p}}if_drone_check=>c_reason-payload. + ELSEIF iv_distance <= 0 OR iv_distance > c_max_distance. + rv_reason = {{p}}if_drone_check=>c_reason-distance. + ELSEIF iv_wind_speed > c_max_wind. + rv_reason = {{p}}if_drone_check=>c_reason-wind. + ELSEIF iv_battery < required_battery( iv_distance ). + rv_reason = {{p}}if_drone_check=>c_reason-battery. + ELSEIF iv_in_zone = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-zone. + ELSEIF iv_daylight = abap_false. + rv_reason = {{p}}if_drone_check=>c_reason-light. + ELSE. + rv_reason = {{p}}if_drone_check=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD required_battery. + IF iv_distance <= 0. + RETURN. + ENDIF. + DATA lv_km TYPE i. + lv_km = iv_distance. + IF lv_km < iv_distance. + lv_km = lv_km + 1. + ENDIF. + rv_percent = lv_km * c_percent_per_km + c_reserve. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1306/reference/drone_checker.testclasses.abap b/tasks_gen/train/G1306/reference/drone_checker.testclasses.abap new file mode 100644 index 0000000..c7004d3 --- /dev/null +++ b/tasks_gen/train/G1306/reference/drone_checker.testclasses.abap @@ -0,0 +1,110 @@ +CLASS ltc_drone_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_drone_check. + DATA mo_drone TYPE REF TO {{p}}drone_checker. + + METHODS setup. + METHODS valid_request_is_ok FOR TESTING. + METHODS heavy_payload_is_rejected FOR TESTING. + METHODS strong_wind_is_rejected FOR TESTING. + METHODS low_battery_is_rejected FOR TESTING. + METHODS required_battery_is_rounded_up FOR TESTING. + METHODS zone_and_light_are_checked FOR TESTING. + METHODS reason_order_is_kept FOR TESTING. +ENDCLASS. + + +CLASS ltc_drone_checker IMPLEMENTATION. + METHOD setup. + mo_drone = NEW {{p}}drone_checker( ). + mo_cut = mo_drone. + ENDMETHOD. + + METHOD valid_request_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-ok + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD heavy_payload_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '5.01' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD strong_wind_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-wind + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '12.01' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD low_battery_is_rejected. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-battery + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 29 + iv_in_zone = abap_true + iv_daylight = abap_true ) ). + ENDMETHOD. + + METHOD required_battery_is_rounded_up. + cl_abap_unit_assert=>assert_equals( exp = 26 act = mo_drone->required_battery( '7.50' ) ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = mo_drone->required_battery( '10.00' ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_drone->required_battery( '0.00' ) ). + ENDMETHOD. + + METHOD zone_and_light_are_checked. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-zone + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_false + iv_daylight = abap_true ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-light + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '10.00' + iv_wind_speed = '5.00' + iv_battery = 30 + iv_in_zone = abap_true + iv_daylight = abap_false ) ). + ENDMETHOD. + + METHOD reason_order_is_kept. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-payload + act = mo_cut->check( iv_payload = '6.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_drone_check=>c_reason-distance + act = mo_cut->check( iv_payload = '2.00' + iv_distance = '25.00' + iv_wind_speed = '20.00' + iv_battery = 0 + iv_in_zone = abap_false + iv_daylight = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1306/seed/if_drone_check.intf.abap b/tasks_gen/train/G1306/seed/if_drone_check.intf.abap new file mode 100644 index 0000000..c67d0b8 --- /dev/null +++ b/tasks_gen/train/G1306/seed/if_drone_check.intf.abap @@ -0,0 +1,25 @@ +INTERFACE {{p}}if_drone_check PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_weight TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_distance TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_speed TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_percent TYPE i. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + payload TYPE ty_reason VALUE 'PAYLOAD', + distance TYPE ty_reason VALUE 'DISTANCE', + wind TYPE ty_reason VALUE 'WIND', + battery TYPE ty_reason VALUE 'BATTERY', + zone TYPE ty_reason VALUE 'ZONE', + light TYPE ty_reason VALUE 'LIGHT', + END OF c_reason. + METHODS check + IMPORTING iv_payload TYPE ty_weight + iv_distance TYPE ty_distance + iv_wind_speed TYPE ty_speed + iv_battery TYPE ty_percent + iv_in_zone TYPE abap_bool + iv_daylight TYPE abap_bool + RETURNING VALUE(rv_reason) TYPE ty_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1306/spec.md b/tasks_gen/train/G1306/spec.md new file mode 100644 index 0000000..e22683a --- /dev/null +++ b/tasks_gen/train/G1306/spec.md @@ -0,0 +1 @@ +Hello, please create the class {{P}}DRONE_CHECKER in package $TMP. The class must be public, final, and have a public constructor without parameters. It must implement the interface {{P}}IF_DRONE_CHECK. The interface {{P}}IF_DRONE_CHECK exists in package $TMP and is active. It has the types TY_REASON (character, length 10), TY_WEIGHT (packed number, length 8, 2 decimals), TY_DISTANCE (packed number, length 8, 2 decimals), TY_SPEED (packed number, length 8, 2 decimals), and TY_PERCENT (integer). It has the constant structure C_REASON with the components OK, PAYLOAD, DISTANCE, WIND, BATTERY, ZONE, and LIGHT. It has one method CHECK with IMPORTING parameters iv_payload TYPE ty_weight, iv_distance TYPE ty_distance, iv_wind_speed TYPE ty_speed, iv_battery TYPE ty_percent, iv_in_zone TYPE abap_bool, iv_daylight TYPE abap_bool, and RETURNING VALUE(rv_reason) TYPE ty_reason. The class must have the public method REQUIRED_BATTERY with IMPORTING iv_distance TYPE {{P}}IF_DRONE_CHECK=>TY_DISTANCE and RETURNING VALUE(rv_percent) TYPE {{P}}IF_DRONE_CHECK=>TY_PERCENT. Do not add other public methods. The payload is in kilograms, the distance in kilometres, the wind speed in metres per second, and the battery charge in percent. The check applies the rules in this sequence and returns the reason of the first rule that fails. If the payload is less than or equal to zero, or greater than 5.00, return PAYLOAD. If the distance is less than or equal to zero, or greater than 20.00, return DISTANCE. If the wind speed is greater than 12.00, return WIND. If the battery charge is less than the charge that the flight needs, return BATTERY. If iv_in_zone is abap_false, return ZONE. If iv_daylight is abap_false, return LIGHT. Else return OK. The charge that a flight needs is the result of REQUIRED_BATTERY. If the distance is less than or equal to zero, the result is 0. Else the result is 2 percent for each started kilometre plus a reserve of 10 percent. A started kilometre counts as a full kilometre. The release target is SAP_BASIS 816 (ABAP Platform 2025). Follow Clean ABAP. Keep the method length below 40 statements. Do not use global variables. Do not write comments that restate the code. Do not change {{P}}IF_DRONE_CHECK. The class must be active and have no syntax error. The hidden tests must pass. Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1306/task.json b/tasks_gen/train/G1306/task.json new file mode 100644 index 0000000..23b8633 --- /dev/null +++ b/tasks_gen/train/G1306/task.json @@ -0,0 +1,53 @@ +{ + "id": "G1306", + "category": "K", + "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_DRONE_CHECK", + "file": "seed/if_drone_check.intf.abap", + "description": "Drone flight check contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}DRONE_CHECKER", + "implements": "{{P}}IF_DRONE_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_DRONE_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}DRONE_CHECKER", + "file": "reference/drone_checker.clas.abap", + "description": "Drone flight checker", + "testclasses_file": "reference/drone_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ], + "base_task": "G1003", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1307/generation.json b/tasks_gen/train/G1307/generation.json new file mode 100644 index 0000000..15b1718 --- /dev/null +++ b/tasks_gen/train/G1307/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1307", + "pool": "train", + "category": "K", + "base_task": "G1012", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1307/hidden/t16_hidden.clas.abap b/tasks_gen/train/G1307/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..81822ea --- /dev/null +++ b/tasks_gen/train/G1307/hidden/t16_hidden.clas.abap @@ -0,0 +1,99 @@ +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 only_farms_above_limit FOR TESTING. + METHODS total_litres FOR TESTING. + METHODS delivery_count FOR TESTING. + METHODS farm_below_limit_hidden FOR TESTING. + METHODS farm_at_limit_hidden FOR TESTING. + METHODS farm_just_above_limit FOR TESTING. + METHODS no_deliv_farm_hidden FOR TESTING. + METHODS unknown_farm_hidden FOR TESTING. + METHODS farmer_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MILK_TOTAL' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_farm = VALUE #( ( farm_id = 'F1' farmer = 'ALPHA' region = 'NORTH' ) + ( farm_id = 'F2' farmer = 'BETA' region = 'NORTH' ) + ( farm_id = 'F3' farmer = 'GAMMA' region = 'SOUTH' ) + ( farm_id = 'F4' farmer = 'DELTA' region = 'SOUTH' ) + ( farm_id = 'F5' farmer = 'EPSILON' region = 'EAST' ) ). + lt_milk = VALUE #( ( farm_id = 'F1' deliv_date = '20240101' litres = '300.0' ) + ( farm_id = 'F1' deliv_date = '20240102' litres = '250.5' ) + ( farm_id = 'F2' deliv_date = '20240101' litres = '200.0' ) + ( farm_id = 'F2' deliv_date = '20240102' litres = '100.0' ) + ( farm_id = 'F3' deliv_date = '20240101' litres = '500.0' ) + ( farm_id = 'F5' deliv_date = '20240101' litres = '250.0' ) + ( farm_id = 'F5' deliv_date = '20240102' litres = '250.5' ) + ( farm_id = 'X9' deliv_date = '20240101' litres = '600.0' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + ENDMETHOD. + + METHOD only_farms_above_limit. + SELECT COUNT(*) FROM {{p}}i_milk_total INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD total_litres. + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'F1' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '550.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD delivery_count. + SELECT SINGLE delivery_count FROM {{p}}i_milk_total WHERE farm_id = 'F1' + INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD farm_below_limit_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_at_limit_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_just_above_limit. + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'F5' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '500.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. + + METHOD no_deliv_farm_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'F4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD unknown_farm_hidden. + SELECT COUNT(*) FROM {{p}}i_milk_total WHERE farm_id = 'X9' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farmer_shown. + SELECT SINGLE farmer FROM {{p}}i_milk_total WHERE farm_id = 'F1' INTO @DATA(lv_farmer). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lv_farmer ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1307/reference/i_milk_total.ddls.asddls b/tasks_gen/train/G1307/reference/i_milk_total.ddls.asddls new file mode 100644 index 0000000..4b1954c --- /dev/null +++ b/tasks_gen/train/G1307/reference/i_milk_total.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Milk totals per farm' +define view entity {{P}}I_MILK_TOTAL + as select from {{p}}milk as milk + inner join {{p}}farm as farm on farm.farm_id = milk.farm_id +{ + key milk.farm_id as farm_id, + farm.farmer as farmer, + sum( milk.litres ) as total_litres, + count( * ) as delivery_count +} +group by + milk.farm_id, + farm.farmer +having + sum( milk.litres ) > 500 diff --git a/tasks_gen/train/G1307/reference/t16_test.clas.abap b/tasks_gen/train/G1307/reference/t16_test.clas.abap new file mode 100644 index 0000000..3cdbaf5 --- /dev/null +++ b/tasks_gen/train/G1307/reference/t16_test.clas.abap @@ -0,0 +1,66 @@ +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 total_of_one_farm FOR TESTING. + METHODS farm_below_limit FOR TESTING. + METHODS farm_just_above_limit FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_MILK_TOTAL' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD total_of_one_farm. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'A' farmer = 'ANN' region = 'EAST' ) ). + lt_milk = VALUE #( ( farm_id = 'A' deliv_date = '20240101' litres = '400.0' ) + ( farm_id = 'A' deliv_date = '20240102' litres = '150.5' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT SINGLE total_litres, delivery_count FROM {{p}}i_milk_total WHERE farm_id = 'A' + INTO @DATA(ls_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '550.5' ) + act = CONV decfloat34( ls_total-total_litres ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_total-delivery_count ). + ENDMETHOD. + + METHOD farm_below_limit. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'B' farmer = 'BOB' region = 'WEST' ) ). + lt_milk = VALUE #( ( farm_id = 'B' deliv_date = '20240101' litres = '100.0' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT COUNT(*) FROM {{p}}i_milk_total INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD farm_just_above_limit. + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = 'C' farmer = 'CARL' region = 'WEST' ) ). + lt_milk = VALUE #( ( farm_id = 'C' deliv_date = '20240101' litres = '250.0' ) + ( farm_id = 'C' deliv_date = '20240102' litres = '250.5' ) ). + go_env->insert_test_data( lt_farm ). + go_env->insert_test_data( lt_milk ). + SELECT SINGLE total_litres FROM {{p}}i_milk_total WHERE farm_id = 'C' + INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '500.5' ) + act = CONV decfloat34( lv_total ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1307/seed/farm.tabl.asabap b/tasks_gen/train/G1307/seed/farm.tabl.asabap new file mode 100644 index 0000000..35baf34 --- /dev/null +++ b/tasks_gen/train/G1307/seed/farm.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Farm master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}farm { + key client : abap.clnt not null; + key farm_id : abap.char(10) not null; + farmer : abap.char(20); + region : abap.char(10); +} diff --git a/tasks_gen/train/G1307/seed/milk.tabl.asabap b/tasks_gen/train/G1307/seed/milk.tabl.asabap new file mode 100644 index 0000000..7553baa --- /dev/null +++ b/tasks_gen/train/G1307/seed/milk.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Milk delivery' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}milk { + key client : abap.clnt not null; + key farm_id : abap.char(10) not null; + key deliv_date : abap.dats not null; + litres : abap.dec(10,1); +} diff --git a/tasks_gen/train/G1307/spec.md b/tasks_gen/train/G1307/spec.md new file mode 100644 index 0000000..62f0645 --- /dev/null +++ b/tasks_gen/train/G1307/spec.md @@ -0,0 +1,20 @@ +Create the CDS view entity {{P}}I_MILK_TOTAL in package $TMP. Use source tables {{P}}FARM and {{P}}MILK. The report and OData service read this view. The calculation must run in the database. + +Use these element names exactly: FARM_ID (key), FARMER, TOTAL_LITRES, DELIVERY_COUNT. Set the authorization check to #NOT_REQUIRED. + +Business rules: +1. Show one row for each farm with total litres greater than 500. Do not show a farm with 500 litres or less. Do not show a farm without a delivery. Do not show a delivery whose farm is not in the farm master. +2. TOTAL_LITRES is the sum of LITRES for all deliveries of the farm. Use one decimal. +3. DELIVERY_COUNT is the number of deliveries of the farm. +4. FARMER is the farmer of the farm. +5. Group rows by farm. There is exactly one row for each farm. + +Constraints: +- Release target: 8.16 (SAP_BASIS 816). +- Use Clean ABAP. No ATC priority 1 or 2 findings. +- Do not change {{P}}FARM and {{P}}MILK. + +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/G1307/task.json b/tasks_gen/train/G1307/task.json new file mode 100644 index 0000000..7ec511e --- /dev/null +++ b/tasks_gen/train/G1307/task.json @@ -0,0 +1,67 @@ +{ + "id": "G1307", + "category": "K", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}FARM", + "file": "seed/farm.tabl.asabap", + "description": "Farm master" + }, + { + "type": "TABL", + "name": "{{P}}MILK", + "file": "seed/milk.tabl.asabap", + "description": "Milk delivery" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_MILK_TOTAL", + "fields": [ + "FARM_ID", + "FARMER", + "TOTAL_LITRES", + "DELIVERY_COUNT" + ] + } + ], + "out_of_scope": [ + "{{P}}FARM", + "{{P}}MILK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_MILK_TOTAL", + "file": "reference/i_milk_total.ddls.asddls", + "description": "Milk totals per farm" + }, + { + "type": "CLAS", + "name": "{{P}}T16_TEST", + "file": "reference/t16_test.clas.abap", + "description": "T16 own tests" + } + ], + "craft_checks": [], + "base_task": "G1012", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1308/generation.json b/tasks_gen/train/G1308/generation.json new file mode 100644 index 0000000..52f0e2b --- /dev/null +++ b/tasks_gen/train/G1308/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1308", + "pool": "train", + "category": "K", + "base_task": "G1004", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1308/hidden/balloon_hidden.clas.abap b/tasks_gen/train/G1308/hidden/balloon_hidden.clas.abap new file mode 100644 index 0000000..c02f574 --- /dev/null +++ b/tasks_gen/train/G1308/hidden/balloon_hidden.clas.abap @@ -0,0 +1,169 @@ +CLASS {{p}}balloon_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}balloon_checker. + + METHODS setup. + METHODS free_payload + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i. + + METHODS valid_flight_is_released FOR TESTING. + METHODS crew_above_limit FOR TESTING. + METHODS weight_above_limit FOR TESTING. + METHODS wind_above_limit FOR TESTING. + METHODS cloud_base_below_limit FOR TESTING. + METHODS crew_is_checked_first FOR TESTING. + METHODS weight_is_checked_second FOR TESTING. + METHODS wind_is_checked_third FOR TESTING. + METHODS limits_are_accepted FOR TESTING. + METHODS free_payload_is_returned FOR TESTING. +ENDCLASS. + + +CLASS {{p}}balloon_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}balloon_checker( ). + ENDMETHOD. + + METHOD free_payload. + TRY. + rv_free_kg = mo_cut->check_flight( iv_passengers = iv_passengers + iv_weight_kg = iv_weight_kg + iv_wind_kmh = iv_wind_kmh + iv_cloud_base_m = iv_cloud_base_m ). + CATCH {{p}}cx_balloon_flight. + rv_free_kg = -1. + ENDTRY. + ENDMETHOD. + + METHOD valid_flight_is_released. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD crew_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. + + METHOD weight_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_weight INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Weight 700 kg exceeds the limit 640 kg' + act = lv_text ). + ENDMETHOD. + + METHOD wind_above_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 2000 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_wind INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Wind 25 km/h exceeds the limit 18 km/h' + act = lv_text ). + ENDMETHOD. + + METHOD cloud_base_below_limit. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 200 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_cloud INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Cloud base 200 m is below the limit 300 m' + act = lv_text ). + ENDMETHOD. + + METHOD crew_is_checked_first. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. + + METHOD weight_is_checked_second. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_weight INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Weight 700 kg exceeds the limit 640 kg' + act = lv_text ). + ENDMETHOD. + + METHOD wind_is_checked_third. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_wind INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Wind 25 km/h exceeds the limit 18 km/h' + act = lv_text ). + ENDMETHOD. + + METHOD limits_are_accepted. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = free_payload( iv_passengers = 8 iv_weight_kg = 640 + iv_wind_kmh = 18 iv_cloud_base_m = 300 ) ). + ENDMETHOD. + + METHOD free_payload_is_returned. + cl_abap_unit_assert=>assert_equals( + exp = 40 + act = free_payload( iv_passengers = 2 iv_weight_kg = 600 + iv_wind_kmh = 12 iv_cloud_base_m = 1200 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/balloon_checker.clas.abap b/tasks_gen/train/G1308/reference/balloon_checker.clas.abap new file mode 100644 index 0000000..27e34a7 --- /dev/null +++ b/tasks_gen/train/G1308/reference/balloon_checker.clas.abap @@ -0,0 +1,52 @@ +CLASS {{p}}balloon_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS check_flight + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i + RAISING {{p}}cx_balloon_flight. + + PRIVATE SECTION. + CONSTANTS: + c_max_passengers TYPE i VALUE 8, + c_max_weight_kg TYPE i VALUE 640, + c_max_wind_kmh TYPE i VALUE 18, + c_min_cloud_base_m TYPE i VALUE 300. +ENDCLASS. + + +CLASS {{p}}balloon_checker IMPLEMENTATION. + METHOD check_flight. + IF iv_passengers > c_max_passengers. + RAISE EXCEPTION TYPE {{p}}cx_balloon_crew + EXPORTING + iv_passengers = iv_passengers + iv_limit = c_max_passengers. + ENDIF. + + IF iv_weight_kg > c_max_weight_kg. + RAISE EXCEPTION TYPE {{p}}cx_balloon_weight + EXPORTING + iv_weight_kg = iv_weight_kg + iv_limit_kg = c_max_weight_kg. + ENDIF. + + IF iv_wind_kmh > c_max_wind_kmh. + RAISE EXCEPTION TYPE {{p}}cx_balloon_wind + EXPORTING + iv_wind_kmh = iv_wind_kmh + iv_limit_kmh = c_max_wind_kmh. + ENDIF. + + IF iv_cloud_base_m < c_min_cloud_base_m. + RAISE EXCEPTION TYPE {{p}}cx_balloon_cloud + EXPORTING + iv_cloud_base_m = iv_cloud_base_m + iv_limit_m = c_min_cloud_base_m. + ENDIF. + + rv_free_kg = c_max_weight_kg - iv_weight_kg. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/balloon_checker.testclasses.abap b/tasks_gen/train/G1308/reference/balloon_checker.testclasses.abap new file mode 100644 index 0000000..09ad444 --- /dev/null +++ b/tasks_gen/train/G1308/reference/balloon_checker.testclasses.abap @@ -0,0 +1,89 @@ +CLASS ltc_balloon_checker DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}balloon_checker. + + METHODS setup. + METHODS free_payload + IMPORTING iv_passengers TYPE i + iv_weight_kg TYPE i + iv_wind_kmh TYPE i + iv_cloud_base_m TYPE i + RETURNING VALUE(rv_free_kg) TYPE i. + + METHODS released_flight FOR TESTING. + METHODS crew_limit FOR TESTING. + METHODS weight_limit FOR TESTING. + METHODS wind_limit FOR TESTING. + METHODS cloud_limit FOR TESTING. + METHODS check_sequence FOR TESTING. +ENDCLASS. + + +CLASS ltc_balloon_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}balloon_checker( ). + ENDMETHOD. + + METHOD free_payload. + TRY. + rv_free_kg = mo_cut->check_flight( iv_passengers = iv_passengers + iv_weight_kg = iv_weight_kg + iv_wind_kmh = iv_wind_kmh + iv_cloud_base_m = iv_cloud_base_m ). + CATCH {{p}}cx_balloon_flight. + rv_free_kg = -1. + ENDTRY. + ENDMETHOD. + + METHOD released_flight. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD crew_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 9 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD weight_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 700 + iv_wind_kmh = 10 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD wind_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 25 iv_cloud_base_m = 2000 ) ). + ENDMETHOD. + + METHOD cloud_limit. + cl_abap_unit_assert=>assert_equals( + exp = -1 + act = free_payload( iv_passengers = 4 iv_weight_kg = 500 + iv_wind_kmh = 10 iv_cloud_base_m = 200 ) ). + ENDMETHOD. + + METHOD check_sequence. + DATA lv_text TYPE string. + TRY. + mo_cut->check_flight( iv_passengers = 9 iv_weight_kg = 700 + iv_wind_kmh = 25 iv_cloud_base_m = 100 ). + cl_abap_unit_assert=>fail( msg = 'No exception was raised' ). + CATCH {{p}}cx_balloon_crew INTO DATA(lo_exc). + lv_text = lo_exc->mv_text. + CATCH {{p}}cx_balloon_flight. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + cl_abap_unit_assert=>assert_equals( exp = 'Passengers 9 exceeds the limit 8' + act = lv_text ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/cx_balloon_cloud.clas.abap b/tasks_gen/train/G1308/reference/cx_balloon_cloud.clas.abap new file mode 100644 index 0000000..6581da7 --- /dev/null +++ b/tasks_gen/train/G1308/reference/cx_balloon_cloud.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_cloud DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_cloud_base_m TYPE i + iv_limit_m TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_cloud IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Cloud base { iv_cloud_base_m } m is below the limit { iv_limit_m } m| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/cx_balloon_crew.clas.abap b/tasks_gen/train/G1308/reference/cx_balloon_crew.clas.abap new file mode 100644 index 0000000..1f2c878 --- /dev/null +++ b/tasks_gen/train/G1308/reference/cx_balloon_crew.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_crew DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_passengers TYPE i + iv_limit TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_crew IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Passengers { iv_passengers } exceeds the limit { iv_limit }| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/cx_balloon_weight.clas.abap b/tasks_gen/train/G1308/reference/cx_balloon_weight.clas.abap new file mode 100644 index 0000000..2bd4fae --- /dev/null +++ b/tasks_gen/train/G1308/reference/cx_balloon_weight.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_weight DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_weight_kg TYPE i + iv_limit_kg TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_weight IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Weight { iv_weight_kg } kg exceeds the limit { iv_limit_kg } kg| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/reference/cx_balloon_wind.clas.abap b/tasks_gen/train/G1308/reference/cx_balloon_wind.clas.abap new file mode 100644 index 0000000..cecf001 --- /dev/null +++ b/tasks_gen/train/G1308/reference/cx_balloon_wind.clas.abap @@ -0,0 +1,16 @@ +CLASS {{p}}cx_balloon_wind DEFINITION PUBLIC INHERITING FROM {{p}}cx_balloon_flight FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS constructor + IMPORTING iv_wind_kmh TYPE i + iv_limit_kmh TYPE i. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_wind IMPLEMENTATION. + METHOD constructor. + super->constructor( iv_text = |Wind { iv_wind_kmh } km/h exceeds the limit { iv_limit_kmh } km/h| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/seed/cx_balloon_flight.clas.abap b/tasks_gen/train/G1308/seed/cx_balloon_flight.clas.abap new file mode 100644 index 0000000..2c8c407 --- /dev/null +++ b/tasks_gen/train/G1308/seed/cx_balloon_flight.clas.abap @@ -0,0 +1,18 @@ +CLASS {{p}}cx_balloon_flight DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + + METHODS constructor + IMPORTING iv_text TYPE string. + + PRIVATE SECTION. + CONSTANTS c_dummy TYPE i VALUE 0. +ENDCLASS. + + +CLASS {{p}}cx_balloon_flight IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = iv_text. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1308/spec.md b/tasks_gen/train/G1308/spec.md new file mode 100644 index 0000000..6fccac1 --- /dev/null +++ b/tasks_gen/train/G1308/spec.md @@ -0,0 +1,13 @@ +Hello, we need a check for hot-air balloon flights before launch. The flight office calls this check for each planned flight. The check must stop a flight when a limit is exceeded. The caller needs one exception for each reason. + +The exception class {{P}}CX_BALLOON_FLIGHT already exists in package $TMP. It is active. It inherits from CX_STATIC_CHECK. It has a public read-only attribute MV_TEXT of type string and a constructor with importing parameter IV_TEXT of type string. The constructor writes IV_TEXT to MV_TEXT. The flight office knows four figures: the number of passengers, the total take-off weight in kilograms, the wind speed in kilometres per hour, and the height of the cloud base in metres. + +Please create the class {{P}}BALLOON_CHECKER in package $TMP. The class is public and final. It can be created without constructor parameters. It has the public method CHECK_FLIGHT with these parameters: importing IV_PASSENGERS type i, importing IV_WEIGHT_KG type i, importing IV_WIND_KMH type i, importing IV_CLOUD_BASE_M type i, returning RV_FREE_KG type i, and raising {{P}}CX_BALLOON_FLIGHT. Do not add other public methods. + +Also create these exception classes in package $TMP. Each class inherits from {{P}}CX_BALLOON_FLIGHT. {{P}}CX_BALLOON_CREW has a constructor with importing IV_PASSENGERS type i and IV_LIMIT type i. {{P}}CX_BALLOON_WEIGHT has a constructor with importing IV_WEIGHT_KG type i and IV_LIMIT_KG type i. {{P}}CX_BALLOON_WIND has a constructor with importing IV_WIND_KMH type i and IV_LIMIT_KMH type i. {{P}}CX_BALLOON_CLOUD has a constructor with importing IV_CLOUD_BASE_M type i and IV_LIMIT_M type i. Each constructor writes the message text to MV_TEXT. The rules below give the text of each exception. + +The method CHECK_FLIGHT applies the rules in this sequence. It raises the exception of the first rule that fails. If no rule fails, the method returns the free payload. First, check the passengers. The limit is 8. If IV_PASSENGERS is greater than 8, raise {{P}}CX_BALLOON_CREW. The message text is "Passengers exceeds the limit ". For example, 12 passengers give the text "Passengers 12 exceeds the limit 8". Second, check the weight. The limit is 640 kilograms. If IV_WEIGHT_KG is greater than 640, raise {{P}}CX_BALLOON_WEIGHT. The message text is "Weight kg exceeds the limit kg". For example, 700 kilograms give the text "Weight 700 kg exceeds the limit 640 kg". Third, check the wind. The limit is 18 kilometres per hour. If IV_WIND_KMH is greater than 18, raise {{P}}CX_BALLOON_WIND. The message text is "Wind km/h exceeds the limit km/h". For example, 25 kilometres per hour give the text "Wind 25 km/h exceeds the limit 18 km/h". Fourth, check the cloud base. The limit is 300 metres. If IV_CLOUD_BASE_M is less than 300, raise {{P}}CX_BALLOON_CLOUD. The message text is "Cloud base m is below the limit m". For example, 200 metres give the text "Cloud base 200 m is below the limit 300 m". Finally, if no rule fails, the free payload is 640 minus IV_WEIGHT_KG. Return the free payload in RV_FREE_KG. + +The release target is ABAP Platform 2025 (SAP_BASIS 816). Follow Clean ABAP. Keep the method length below 40 statements. Do not use global variables. Do not write comments that restate the code. Do not change {{P}}CX_BALLOON_FLIGHT. + +The classes must be active and have no syntax error. The hidden tests must pass. Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1308/task.json b/tasks_gen/train/G1308/task.json new file mode 100644 index 0000000..8ea208b --- /dev/null +++ b/tasks_gen/train/G1308/task.json @@ -0,0 +1,92 @@ +{ + "id": "G1308", + "category": "K", + "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_BALLOON_FLIGHT", + "file": "seed/cx_balloon_flight.clas.abap", + "description": "Base exception class of a rejected balloon flight" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}BALLOON_CHECKER" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CREW" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WEIGHT" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WIND" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CLOUD" + } + ], + "out_of_scope": [ + "{{P}}CX_BALLOON_FLIGHT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}BALLOON_HIDDEN", + "file": "hidden/balloon_hidden.clas.abap", + "description": "Hidden tests for the balloon flight check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CREW", + "file": "reference/cx_balloon_crew.clas.abap", + "description": "Too many passengers" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WEIGHT", + "file": "reference/cx_balloon_weight.clas.abap", + "description": "Weight above the limit" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_WIND", + "file": "reference/cx_balloon_wind.clas.abap", + "description": "Wind above the limit" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BALLOON_CLOUD", + "file": "reference/cx_balloon_cloud.clas.abap", + "description": "Cloud base below the limit" + }, + { + "type": "CLAS", + "name": "{{P}}BALLOON_CHECKER", + "file": "reference/balloon_checker.clas.abap", + "description": "Balloon flight check with class-based exceptions", + "testclasses_file": "reference/balloon_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ], + "base_task": "G1004", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1309/generation.json b/tasks_gen/train/G1309/generation.json new file mode 100644 index 0000000..f7a389f --- /dev/null +++ b/tasks_gen/train/G1309/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1309", + "pool": "train", + "category": "K", + "base_task": "G1020", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1309/hidden/t33_hidden.clas.abap b/tasks_gen/train/G1309/hidden/t33_hidden.clas.abap new file mode 100644 index 0000000..c7adebf --- /dev/null +++ b/tasks_gen/train/G1309/hidden/t33_hidden.clas.abap @@ -0,0 +1,86 @@ +CLASS {{p}}t33_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_grade FOR TESTING. + METHODS medium_grade FOR TESTING. + METHODS large_grade FOR TESTING. + METHODS height_in_metres FOR TESTING. + METHODS grade_text_value FOR TESTING. + METHODS row_count FOR TESTING. + METHODS species_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t33_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PLANT_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_plant TYPE STANDARD TABLE OF {{p}}plant WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_plant = VALUE #( ( tree_id = 'T1' species = 'OAK' height_cm = 99 ) + ( tree_id = 'T2' species = 'OAK' height_cm = 150 ) + ( tree_id = 'T3' species = 'PINE' height_cm = 200 ) + ( tree_id = 'T4' species = 'PINE' height_cm = 100 ) + ( tree_id = 'T5' species = 'BIRCH' height_cm = 199 ) + ( tree_id = 'T6' species = 'BIRCH' height_cm = 250 ) ). + go_env->insert_test_data( lt_plant ). + ENDMETHOD. + + METHOD small_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T1' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lv_grade ). + ENDMETHOD. + + METHOD medium_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T2' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T4' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T5' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + ENDMETHOD. + + METHOD large_grade. + DATA lv_grade TYPE c LENGTH 1. + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_grade ). + SELECT SINGLE grade FROM {{p}}i_plant_grade WHERE tree_id = 'T6' INTO @lv_grade. + cl_abap_unit_assert=>assert_equals( exp = 'L' act = lv_grade ). + ENDMETHOD. + + METHOD height_in_metres. + SELECT SINGLE height_m FROM {{p}}i_plant_grade WHERE tree_id = 'T2' INTO @DATA(lv_height). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.50' ) + act = CONV decfloat34( lv_height ) ). + ENDMETHOD. + + METHOD grade_text_value. + DATA lv_text TYPE c LENGTH 30. + SELECT SINGLE grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'T1' INTO @lv_text. + cl_abap_unit_assert=>assert_equals( exp = 'S-OAK' act = lv_text ). + SELECT SINGLE grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @lv_text. + cl_abap_unit_assert=>assert_equals( exp = 'L-PINE' act = lv_text ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_plant_grade INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_count ). + ENDMETHOD. + + METHOD species_shown. + SELECT SINGLE species FROM {{p}}i_plant_grade WHERE tree_id = 'T3' INTO @DATA(lv_species). + cl_abap_unit_assert=>assert_equals( exp = 'PINE' act = lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1309/reference/i_plant_grade.ddls.asddls b/tasks_gen/train/G1309/reference/i_plant_grade.ddls.asddls new file mode 100644 index 0000000..d1edce1 --- /dev/null +++ b/tasks_gen/train/G1309/reference/i_plant_grade.ddls.asddls @@ -0,0 +1,21 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Grade of a tree sapling' +define view entity {{P}}I_PLANT_GRADE + as select from {{p}}plant as sapling +{ + key sapling.tree_id as tree_id, + sapling.species as species, + cast( sapling.height_cm as abap.dec( 15, 2 ) ) + / 100 as height_m, + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end as grade, + concat( concat( + case when sapling.height_cm < 100 then 'S' + when sapling.height_cm < 200 then 'M' + else 'L' + end, + '-' ), + sapling.species ) as grade_text +} diff --git a/tasks_gen/train/G1309/reference/t33_test.clas.abap b/tasks_gen/train/G1309/reference/t33_test.clas.abap new file mode 100644 index 0000000..9645671 --- /dev/null +++ b/tasks_gen/train/G1309/reference/t33_test.clas.abap @@ -0,0 +1,43 @@ +CLASS {{p}}t33_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 grade_and_text FOR TESTING. + METHODS height_in_metres FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t33_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PLANT_GRADE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_plant TYPE STANDARD TABLE OF {{p}}plant WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_plant = VALUE #( ( tree_id = 'A' species = 'OAK' height_cm = 150 ) ). + go_env->insert_test_data( lt_plant ). + ENDMETHOD. + + METHOD grade_and_text. + DATA lv_grade TYPE c LENGTH 1. + DATA lv_text TYPE c LENGTH 30. + SELECT SINGLE grade, grade_text FROM {{p}}i_plant_grade WHERE tree_id = 'A' + INTO (@lv_grade, @lv_text). + cl_abap_unit_assert=>assert_equals( exp = 'M' act = lv_grade ). + cl_abap_unit_assert=>assert_equals( exp = 'M-OAK' act = lv_text ). + ENDMETHOD. + + METHOD height_in_metres. + DATA lv_height TYPE decfloat34. + SELECT SINGLE height_m FROM {{p}}i_plant_grade WHERE tree_id = 'A' INTO @lv_height. + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.50' ) act = lv_height ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1309/seed/plant.tabl.asabap b/tasks_gen/train/G1309/seed/plant.tabl.asabap new file mode 100644 index 0000000..e9dce15 --- /dev/null +++ b/tasks_gen/train/G1309/seed/plant.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Tree sapling' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}plant { + key client : abap.clnt not null; + key tree_id : abap.char(10) not null; + species : abap.char(20); + height_cm : abap.int4; +} diff --git a/tasks_gen/train/G1309/spec.md b/tasks_gen/train/G1309/spec.md new file mode 100644 index 0000000..77def61 --- /dev/null +++ b/tasks_gen/train/G1309/spec.md @@ -0,0 +1,5 @@ +Create the CDS view entity {{P}}I_PLANT_GRADE in package $TMP. It reads tree saplings from {{P}}PLANT. Do not change {{P}}PLANT. The view has these elements with these exact names: TREE_ID (key), SPECIES, HEIGHT_M, GRADE, GRADE_TEXT. Set the authorization check to #NOT_REQUIRED. Use release target 7.58. Use Clean ABAP. Use a CDS view entity, not a database view. + +Show one row for each tree sapling. Do not add rows and do not remove rows. SPECIES is the species of the sapling. HEIGHT_M is the height in metres with 2 decimals. For example, 150 cm is 1.50 m. GRADE is the size class: 'S' when the height is less than 100 cm; 'M' when the height is 100 cm or more and less than 200 cm; 'L' when the height is 200 cm or more. GRADE_TEXT is the grade, then a hyphen, then the species. For example, a pine sapling of grade 'L' has the text 'L-PINE'. + +The calculation must be in the database in the CDS view. The view {{P}}I_PLANT_GRADE must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1309/task.json b/tasks_gen/train/G1309/task.json new file mode 100644 index 0000000..963bfe8 --- /dev/null +++ b/tasks_gen/train/G1309/task.json @@ -0,0 +1,61 @@ +{ + "id": "G1309", + "category": "K", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}PLANT", + "file": "seed/plant.tabl.asabap", + "description": "Tree sapling" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_PLANT_GRADE", + "fields": [ + "TREE_ID", + "SPECIES", + "HEIGHT_M", + "GRADE", + "GRADE_TEXT" + ] + } + ], + "out_of_scope": [ + "{{P}}PLANT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T33_HIDDEN", + "file": "hidden/t33_hidden.clas.abap", + "description": "T33 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_PLANT_GRADE", + "file": "reference/i_plant_grade.ddls.asddls", + "description": "Grade of each tree sapling" + }, + { + "type": "CLAS", + "name": "{{P}}T33_TEST", + "file": "reference/t33_test.clas.abap", + "description": "T33 own tests" + } + ], + "craft_checks": [], + "base_task": "G1020", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1310/generation.json b/tasks_gen/train/G1310/generation.json new file mode 100644 index 0000000..7f90701 --- /dev/null +++ b/tasks_gen/train/G1310/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1310", + "pool": "train", + "category": "K", + "base_task": "G1005", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1310/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1310/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..49291fa --- /dev/null +++ b/tasks_gen/train/G1310/hidden/t02_hidden.clas.abap @@ -0,0 +1,129 @@ +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_credit_check. + METHODS setup. + METHODS too_young FOR TESTING. + METHODS age_eighteen_is_ok FOR TESTING. + METHODS zero_income FOR TESTING. + METHODS two_open_plans FOR TESTING. + METHODS order_above_limit FOR TESTING. + METHODS order_at_limit_is_ok FOR TESTING. + METHODS young_limit_halved FOR TESTING. + METHODS adult_keeps_limit FOR TESTING. + METHODS young_limit_is_floor FOR TESTING. + METHODS plan_reduces_limit FOR TESTING. + METHODS valid_adult FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}credit_checker( ). + ENDMETHOD. + + METHOD too_young. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-age + act = mo_cut->check( iv_age = 17 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD age_eighteen_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 18 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD zero_income. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-income + act = mo_cut->check( iv_age = 30 + iv_income = '0.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD two_open_plans. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-open_plan + act = mo_cut->check( iv_age = 30 + iv_income = '3000.00' + iv_open_plans = 2 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD order_above_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.01' ) ). + ENDMETHOD. + + METHOD order_at_limit_is_ok. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.00' ) ). + ENDMETHOD. + + METHOD young_limit_halved. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD adult_keeps_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 21 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD young_limit_is_floor. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.50' ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.00' ) ). + ENDMETHOD. + + METHOD plan_reduces_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 1 + iv_order_amount = '2500.01' ) ). + ENDMETHOD. + + METHOD valid_adult. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 35 + iv_income = '2000.00' + iv_open_plans = 1 + iv_order_amount = '5500.00' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1310/reference/credit_checker.clas.abap b/tasks_gen/train/G1310/reference/credit_checker.clas.abap new file mode 100644 index 0000000..49d5113 --- /dev/null +++ b/tasks_gen/train/G1310/reference/credit_checker.clas.abap @@ -0,0 +1,59 @@ +CLASS {{p}}credit_checker DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_credit_check. + + PRIVATE SECTION. + CONSTANTS c_income_factor TYPE i VALUE 3. + CONSTANTS c_plan_reduction TYPE {{p}}if_credit_check=>ty_amount VALUE '500.00'. + CONSTANTS c_min_age TYPE i VALUE 18. + CONSTANTS c_full_limit_age TYPE i VALUE 21. + CONSTANTS c_max_open_plans TYPE i VALUE 2. + CONSTANTS c_hundred TYPE i VALUE 100. + + METHODS credit_limit_cents + IMPORTING iv_age TYPE i + iv_income TYPE {{p}}if_credit_check=>ty_amount + iv_open_plans TYPE i + RETURNING VALUE(rv_cents) TYPE i. + METHODS to_cents + IMPORTING iv_amount TYPE {{p}}if_credit_check=>ty_amount + RETURNING VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}credit_checker IMPLEMENTATION. + METHOD {{p}}if_credit_check~check. + IF iv_age < c_min_age. + rv_reason = {{p}}if_credit_check=>c_reason-age. + RETURN. + ENDIF. + IF iv_income <= 0. + rv_reason = {{p}}if_credit_check=>c_reason-income. + RETURN. + ENDIF. + IF iv_open_plans >= c_max_open_plans. + rv_reason = {{p}}if_credit_check=>c_reason-open_plan. + RETURN. + ENDIF. + IF to_cents( iv_order_amount ) > credit_limit_cents( iv_age = iv_age + iv_income = iv_income + iv_open_plans = iv_open_plans ). + rv_reason = {{p}}if_credit_check=>c_reason-limit. + RETURN. + ENDIF. + rv_reason = {{p}}if_credit_check=>c_reason-ok. + ENDMETHOD. + + METHOD credit_limit_cents. + rv_cents = to_cents( iv_income ) * c_income_factor + - to_cents( c_plan_reduction ) * iv_open_plans. + IF iv_age < c_full_limit_age. + rv_cents = rv_cents DIV 2. + rv_cents = rv_cents DIV c_hundred * c_hundred. + ENDIF. + ENDMETHOD. + + METHOD to_cents. + rv_cents = iv_amount * c_hundred. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1310/reference/credit_checker.testclasses.abap b/tasks_gen/train/G1310/reference/credit_checker.testclasses.abap new file mode 100644 index 0000000..e1b8a57 --- /dev/null +++ b/tasks_gen/train/G1310/reference/credit_checker.testclasses.abap @@ -0,0 +1,98 @@ +CLASS ltc_credit_checker DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_credit_check. + METHODS setup. + METHODS too_young FOR TESTING. + METHODS zero_income FOR TESTING. + METHODS two_open_plans FOR TESTING. + METHODS order_above_limit FOR TESTING. + METHODS order_at_limit FOR TESTING. + METHODS young_limit_halved FOR TESTING. + METHODS young_limit_is_floor FOR TESTING. + METHODS valid_adult FOR TESTING. +ENDCLASS. + + +CLASS ltc_credit_checker IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}credit_checker( ). + ENDMETHOD. + + METHOD too_young. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-age + act = mo_cut->check( iv_age = 17 + iv_income = '3000.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD zero_income. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-income + act = mo_cut->check( iv_age = 30 + iv_income = '0.00' + iv_open_plans = 0 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD two_open_plans. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-open_plan + act = mo_cut->check( iv_age = 30 + iv_income = '3000.00' + iv_open_plans = 2 + iv_order_amount = '100.00' ) ). + ENDMETHOD. + + METHOD order_above_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.01' ) ). + ENDMETHOD. + + METHOD order_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 30 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '3000.00' ) ). + ENDMETHOD. + + METHOD young_limit_halved. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '1000.00' + iv_open_plans = 0 + iv_order_amount = '1500.01' ) ). + ENDMETHOD. + + METHOD young_limit_is_floor. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-limit + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.50' ) ). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 20 + iv_income = '2500.50' + iv_open_plans = 0 + iv_order_amount = '3750.00' ) ). + ENDMETHOD. + + METHOD valid_adult. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_credit_check=>c_reason-ok + act = mo_cut->check( iv_age = 35 + iv_income = '2000.00' + iv_open_plans = 1 + iv_order_amount = '5500.00' ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1310/seed/if_credit_check.intf.abap b/tasks_gen/train/G1310/seed/if_credit_check.intf.abap new file mode 100644 index 0000000..2175a55 --- /dev/null +++ b/tasks_gen/train/G1310/seed/if_credit_check.intf.abap @@ -0,0 +1,18 @@ +INTERFACE {{p}}if_credit_check PUBLIC. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 11 DECIMALS 2. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + age TYPE ty_reason VALUE 'AGE', + income TYPE ty_reason VALUE 'INCOME', + open_plan TYPE ty_reason VALUE 'OPEN_PLAN', + limit TYPE ty_reason VALUE 'LIMIT', + END OF c_reason. + METHODS check + IMPORTING iv_age TYPE i + iv_income TYPE ty_amount + iv_open_plans TYPE i + iv_order_amount TYPE ty_amount + RETURNING VALUE(rv_reason) TYPE ty_reason. +ENDINTERFACE. diff --git a/tasks_gen/train/G1310/spec.md b/tasks_gen/train/G1310/spec.md new file mode 100644 index 0000000..a250046 --- /dev/null +++ b/tasks_gen/train/G1310/spec.md @@ -0,0 +1,5 @@ +Hello, please create the class {{P}}CREDIT_CHECKER in package $TMP. The class is public and final. It has a public constructor without parameters. It implements the interface {{P}}IF_CREDIT_CHECK. Do not add other public methods. The interface {{P}}IF_CREDIT_CHECK exists in package $TMP. It is active. It declares the type of the reason, the type of a money amount, the constants of the reason codes, and one method. The method name, the parameter names, and the parameter types are not in this document. Read the interface in the system. The reason constants have the names OK, AGE, INCOME, OPEN_PLAN, and LIMIT. There are no open questions. + +The check receives the age of the customer in years, the net monthly income of the customer, the number of open instalment plans of the customer, and the amount of the new order. Apply the rules in this sequence. Return the reason of the first rule that fails. If the age is less than 18, return AGE. If the net monthly income is zero or less, return INCOME. If the number of open instalment plans is 2 or more, return OPEN_PLAN. Calculate the credit limit. Start with three times the net monthly income. Subtract 500 for each open instalment plan. If the age is less than 21, halve the limit and round the result down to whole euros. For example, 3750.75 becomes 3750.00. If the amount of the new order is more than the credit limit, return LIMIT. Else return OK. + +The release target is SAP_BASIS 8.16 (ABAP Platform 2025). Use Clean ABAP coding standards. Keep the method length below 40 statements. Do not use global variables. Do not write comments that restate the code. Do not change the interface {{P}}IF_CREDIT_CHECK. The class must be active and have no syntax error. The hidden tests must pass. Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1310/task.json b/tasks_gen/train/G1310/task.json new file mode 100644 index 0000000..578dc27 --- /dev/null +++ b/tasks_gen/train/G1310/task.json @@ -0,0 +1,53 @@ +{ + "id": "G1310", + "category": "K", + "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_CREDIT_CHECK", + "file": "seed/if_credit_check.intf.abap", + "description": "Instalment plan check contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CREDIT_CHECKER", + "implements": "{{P}}IF_CREDIT_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_CREDIT_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CREDIT_CHECKER", + "file": "reference/credit_checker.clas.abap", + "description": "Instalment plan credit checker", + "testclasses_file": "reference/credit_checker.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ], + "base_task": "G1005", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1311/generation.json b/tasks_gen/train/G1311/generation.json new file mode 100644 index 0000000..6b5f6f5 --- /dev/null +++ b/tasks_gen/train/G1311/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1311", + "pool": "train", + "category": "K", + "base_task": "G1009", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1311/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1311/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..b6b8796 --- /dev/null +++ b/tasks_gen/train/G1311/hidden/t31_hidden.clas.abap @@ -0,0 +1,140 @@ +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}}fish_auction. + METHODS setup. + METHODS sums_two_species FOR TESTING. + METHODS counts_distinct_boats FOR TESTING. + METHODS ignores_zero_weight FOR TESTING. + METHODS ignores_blank_species FOR TESTING. + METHODS trims_and_uppercases FOR TESTING. + METHODS sorts_by_species FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS all_entries_invalid FOR TESTING. + METHODS best_species_highest FOR TESTING. + METHODS best_species_tie FOR TESTING. + METHODS best_species_none FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}fish_auction( ). + ENDMETHOD. + + METHOD sums_two_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 100 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 50 ) + ( boat_id = 'B1' species = 'HERRING' weight_kg = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 150 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + READ TABLE lt_result INTO ls_row WITH KEY species = 'HERRING'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 1 ). + ENDMETHOD. + + METHOD counts_distinct_boats. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B1' species = 'COD' weight_kg = 20 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 5 ) ) ). + + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 35 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD ignores_zero_weight. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 0 ) + ( boat_id = 'B2' species = 'COD' weight_kg = -5 ) + ( boat_id = 'B3' species = 'COD' weight_kg = 7 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 7 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 1 ). + ENDMETHOD. + + METHOD ignores_blank_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = '' weight_kg = 10 ) + ( boat_id = 'B2' species = ' ' weight_kg = 20 ) + ( boat_id = 'B3' species = 'COD' weight_kg = 5 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 5 ). + ENDMETHOD. + + METHOD trims_and_uppercases. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' cod ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD sorts_by_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 20 ) + ( boat_id = 'B3' species = 'MACKEREL' weight_kg = 5 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 3 ). + READ TABLE lt_result INTO DATA(ls_row) INDEX 1. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'COD' ). + READ TABLE lt_result INTO ls_row INDEX 3. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'TUNA' ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD all_entries_invalid. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD best_species_highest. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA' weight_kg = 40 ) + ( boat_id = 'B3' species = 'HERRING' weight_kg = 25 ) ) ) + exp = 'TUNA' ). + ENDMETHOD. + + METHOD best_species_tie. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 30 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 30 ) ) ) + exp = 'COD' ). + ENDMETHOD. + + METHOD best_species_none. + cl_abap_unit_assert=>assert_initial( mo_cut->best_species( VALUE #( ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1311/reference/fish_auction.clas.abap b/tasks_gen/train/G1311/reference/fish_auction.clas.abap new file mode 100644 index 0000000..323fabc --- /dev/null +++ b/tasks_gen/train/G1311/reference/fish_auction.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}fish_auction DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_species TYPE c LENGTH 10. + TYPES ty_boat_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_catch, + boat_id TYPE ty_boat_id, + species TYPE ty_species, + weight_kg TYPE i, + END OF ty_catch. + TYPES tt_catch TYPE STANDARD TABLE OF ty_catch WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_summary, + species TYPE ty_species, + total_weight TYPE i, + boat_count TYPE i, + END OF ty_summary. + TYPES tt_summary TYPE STANDARD TABLE OF ty_summary WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rt_summary) TYPE tt_summary. + + METHODS best_species + IMPORTING it_catch TYPE tt_catch + RETURNING VALUE(rv_species) TYPE ty_species. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_boat_species, + species TYPE ty_species, + boat_id TYPE ty_boat_id, + END OF ty_boat_species, + tt_boat_species TYPE HASHED TABLE OF ty_boat_species + WITH UNIQUE KEY species boat_id. + + METHODS species_key + IMPORTING iv_species TYPE ty_species + RETURNING VALUE(rv_species) TYPE ty_species. +ENDCLASS. + + +CLASS {{p}}fish_auction IMPLEMENTATION. + + METHOD summarize. + DATA lt_total TYPE SORTED TABLE OF ty_summary WITH UNIQUE KEY species. + DATA lt_boat_species TYPE tt_boat_species. + DATA lv_species TYPE ty_species. + + LOOP AT it_catch INTO DATA(ls_catch). + lv_species = species_key( ls_catch-species ). + IF lv_species IS INITIAL OR ls_catch-weight_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( species = lv_species ) INTO TABLE lt_total. + READ TABLE lt_total WITH TABLE KEY species = lv_species + ASSIGNING . + ENDIF. + -total_weight = -total_weight + ls_catch-weight_kg. + + INSERT VALUE #( species = lv_species boat_id = ls_catch-boat_id ) + INTO TABLE lt_boat_species. + IF sy-subrc = 0. + -boat_count = -boat_count + 1. + ENDIF. + ENDLOOP. + + rt_summary = lt_total. + ENDMETHOD. + + METHOD best_species. + DATA(lt_summary) = summarize( it_catch ). + DATA lv_best_weight TYPE i. + + LOOP AT lt_summary INTO DATA(ls_summary). + IF ls_summary-total_weight > lv_best_weight. + lv_best_weight = ls_summary-total_weight. + rv_species = ls_summary-species. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD species_key. + DATA lv_species TYPE ty_species. + lv_species = iv_species. + SHIFT lv_species LEFT DELETING LEADING space. + rv_species = to_upper( lv_species ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1311/reference/fish_auction.testclasses.abap b/tasks_gen/train/G1311/reference/fish_auction.testclasses.abap new file mode 100644 index 0000000..07d6ef6 --- /dev/null +++ b/tasks_gen/train/G1311/reference/fish_auction.testclasses.abap @@ -0,0 +1,77 @@ +CLASS ltc_fish_auction DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}fish_auction. + METHODS setup. + METHODS sums_and_counts FOR TESTING. + METHODS ignores_invalid_entries FOR TESTING. + METHODS normalizes_species FOR TESTING. + METHODS result_is_sorted FOR TESTING. + METHODS best_species_wins FOR TESTING. + METHODS best_species_tie FOR TESTING. +ENDCLASS. + + +CLASS ltc_fish_auction IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}fish_auction( ). + ENDMETHOD. + + METHOD sums_and_counts. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 100 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 50 ) + ( boat_id = 'B1' species = 'COD' weight_kg = 25 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'COD'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 175 ). + cl_abap_unit_assert=>assert_equals( act = ls_row-boat_count exp = 2 ). + ENDMETHOD. + + METHOD ignores_invalid_entries. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' ' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 0 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 0 ). + ENDMETHOD. + + METHOD normalizes_species. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = ' tuna' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA ' weight_kg = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ). + READ TABLE lt_result INTO DATA(ls_row) WITH KEY species = 'TUNA'. + cl_abap_unit_assert=>assert_equals( act = ls_row-total_weight exp = 30 ). + ENDMETHOD. + + METHOD result_is_sorted. + DATA lt_result TYPE STANDARD TABLE OF {{p}}fish_auction=>ty_summary WITH EMPTY KEY. + lt_result = mo_cut->summarize( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 10 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 10 ) ) ). + + READ TABLE lt_result INTO DATA(ls_row) INDEX 1. + cl_abap_unit_assert=>assert_equals( act = ls_row-species exp = 'COD' ). + ENDMETHOD. + + METHOD best_species_wins. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'COD' weight_kg = 10 ) + ( boat_id = 'B2' species = 'TUNA' weight_kg = 40 ) ) ) + exp = 'TUNA' ). + ENDMETHOD. + + METHOD best_species_tie. + cl_abap_unit_assert=>assert_equals( + act = mo_cut->best_species( VALUE #( + ( boat_id = 'B1' species = 'TUNA' weight_kg = 30 ) + ( boat_id = 'B2' species = 'COD' weight_kg = 30 ) ) ) + exp = 'COD' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1311/spec.md b/tasks_gen/train/G1311/spec.md new file mode 100644 index 0000000..7ad9734 --- /dev/null +++ b/tasks_gen/train/G1311/spec.md @@ -0,0 +1,5 @@ +Create the public final class {{P}}FISH_AUCTION. It has a public constructor without parameters. Define these public types: TY_SPECIES (character 10), TY_BOAT_ID (character 10), TY_CATCH (structure with BOAT_ID type TY_BOAT_ID, SPECIES type TY_SPECIES, WEIGHT_KG type i), TT_CATCH (table type of TY_CATCH; it can hold more than one entry with the same boat ID and species), TY_SUMMARY (structure with SPECIES type TY_SPECIES, TOTAL_WEIGHT type i, BOAT_COUNT type i), and TT_SUMMARY (table type of TY_SUMMARY). Add these public methods: SUMMARIZE importing IT_CATCH type TT_CATCH returning RT_SUMMARY type TT_SUMMARY; BEST_SPECIES importing IT_CATCH type TT_CATCH returning RV_SPECIES type TY_SPECIES. Do not add other public methods. + +SUMMARIZE must: not change IT_CATCH. For each entry, build a species key: remove leading and trailing blanks from SPECIES and convert all letters to upper case. Ignore the entry if the species key is empty. Ignore the entry if WEIGHT_KG is zero or less. Group the remaining entries by the species key. TOTAL_WEIGHT of a group is the sum of WEIGHT_KG of all entries in the group. BOAT_COUNT of a group is the number of different boat IDs in the group. The result has one row for each group. The rows are in ascending order of the species key. If no entry remains, the result is empty. + +BEST_SPECIES must: build the groups with the same rules as SUMMARIZE. Return the species key of the group with the highest TOTAL_WEIGHT. If two groups have the same highest TOTAL_WEIGHT, return the species key that is first in ascending order. If there is no group, return an empty value. diff --git a/tasks_gen/train/G1311/task.json b/tasks_gen/train/G1311/task.json new file mode 100644 index 0000000..aebfdb7 --- /dev/null +++ b/tasks_gen/train/G1311/task.json @@ -0,0 +1,45 @@ +{ + "id": "G1311", + "category": "K", + "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}}FISH_AUCTION" + } + ], + "out_of_scope": [ + "No other object in package $TMP." + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}FISH_AUCTION", + "file": "reference/fish_auction.clas.abap", + "description": "Consolidates the catch of the fishing boats", + "testclasses_file": "reference/fish_auction.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops" + ], + "base_task": "G1009", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1312/generation.json b/tasks_gen/train/G1312/generation.json new file mode 100644 index 0000000..8b65b4e --- /dev/null +++ b/tasks_gen/train/G1312/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1312", + "pool": "train", + "category": "K", + "base_task": "G1010", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1312/hidden/t31_hidden.clas.abap b/tasks_gen/train/G1312/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..59c120d --- /dev/null +++ b/tasks_gen/train/G1312/hidden/t31_hidden.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_screen_basic TYPE ty_price VALUE '89.00'. + CONSTANTS c_high_full TYPE ty_price VALUE '101.00'. + CONSTANTS c_mid_board TYPE ty_price VALUE '163.90'. + CONSTANTS c_minimum TYPE ty_price VALUE '25.00'. + CONSTANTS c_zero TYPE ty_price VALUE '0.00'. + CONSTANTS c_port_basic TYPE ty_price VALUE '49.00'. + CONSTANTS c_camera_express TYPE ty_price VALUE '74.00'. + CONSTANTS c_camera_full TYPE ty_price VALUE '59.20'. + CONSTANTS c_port_high TYPE ty_price VALUE '61.25'. + + DATA mo_cut TYPE REF TO {{p}}phone_repair_pricer. + + METHODS setup. + METHODS screen_basic_plain FOR TESTING. + METHODS screen_high_express_warranty FOR TESTING. + METHODS mid_device_surcharge FOR TESTING. + METHODS battery_minimum_charge FOR TESTING. + METHODS battery_warranty_minimum FOR TESTING. + METHODS unknown_repair_is_zero FOR TESTING. + METHODS unknown_repair_ignores_extras FOR TESTING. + METHODS unknown_device_is_basic FOR TESTING. + METHODS express_adds_fee FOR TESTING. + METHODS warranty_after_express FOR TESTING. + METHODS port_high_no_extras FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}phone_repair_pricer( ). + ENDMETHOD. + + METHOD screen_basic_plain. + cl_abap_unit_assert=>assert_equals( + exp = c_screen_basic + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SCREEN' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD screen_high_express_warranty. + cl_abap_unit_assert=>assert_equals( + exp = c_high_full + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SCREEN' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD mid_device_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_mid_board + act = mo_cut->calculate_price( iv_device = 'MID' + iv_repair = 'BOARD' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD battery_minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD battery_warranty_minimum. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD unknown_repair_is_zero. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SPEAKER' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD unknown_repair_ignores_extras. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SPEAKER' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD unknown_device_is_basic. + cl_abap_unit_assert=>assert_equals( + exp = c_port_basic + act = mo_cut->calculate_price( iv_device = 'ULTRA' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD express_adds_fee. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_express + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD warranty_after_express. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_full + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD port_high_no_extras. + cl_abap_unit_assert=>assert_equals( + exp = c_port_high + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1312/reference/phone_repair_pricer.clas.abap b/tasks_gen/train/G1312/reference/phone_repair_pricer.clas.abap new file mode 100644 index 0000000..c7a38ad --- /dev/null +++ b/tasks_gen/train/G1312/reference/phone_repair_pricer.clas.abap @@ -0,0 +1,105 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. + + PRIVATE SECTION. + CONSTANTS c_base_screen TYPE ty_price VALUE '89.00'. + CONSTANTS c_base_battery TYPE ty_price VALUE '19.00'. + CONSTANTS c_base_board TYPE ty_price VALUE '149.00'. + CONSTANTS c_base_camera TYPE ty_price VALUE '59.00'. + CONSTANTS c_base_port TYPE ty_price VALUE '49.00'. + CONSTANTS c_factor_basic TYPE ty_price VALUE '1.00'. + CONSTANTS c_factor_mid TYPE ty_price VALUE '1.10'. + CONSTANTS c_factor_high TYPE ty_price VALUE '1.25'. + CONSTANTS c_express_fee TYPE ty_price VALUE '15.00'. + CONSTANTS c_warranty_rate TYPE ty_price VALUE '0.80'. + CONSTANTS c_minimum_price TYPE ty_price VALUE '25.00'. + + METHODS base_price + IMPORTING iv_repair TYPE ty_repair + RETURNING VALUE(rv_price) TYPE ty_price. + + METHODS device_factor + IMPORTING iv_device TYPE ty_device + RETURNING VALUE(rv_factor) TYPE ty_price. + + METHODS price_with_extras + IMPORTING iv_price TYPE ty_price + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + rv_price = base_price( iv_repair ). + IF rv_price IS INITIAL. + RETURN. + ENDIF. + + rv_price = rv_price * device_factor( iv_device ). + rv_price = price_with_extras( iv_price = rv_price + iv_express = iv_express + iv_warranty = iv_warranty ). + + IF rv_price < c_minimum_price. + rv_price = c_minimum_price. + ENDIF. + ENDMETHOD. + + METHOD base_price. + CASE iv_repair. + WHEN c_repair_screen. + rv_price = c_base_screen. + WHEN c_repair_battery. + rv_price = c_base_battery. + WHEN c_repair_board. + rv_price = c_base_board. + WHEN c_repair_camera. + rv_price = c_base_camera. + WHEN c_repair_port. + rv_price = c_base_port. + ENDCASE. + ENDMETHOD. + + METHOD device_factor. + CASE iv_device. + WHEN c_device_mid. + rv_factor = c_factor_mid. + WHEN c_device_high. + rv_factor = c_factor_high. + WHEN OTHERS. + rv_factor = c_factor_basic. + ENDCASE. + ENDMETHOD. + + METHOD price_with_extras. + rv_price = iv_price. + IF iv_express = abap_true. + rv_price = rv_price + c_express_fee. + ENDIF. + IF iv_warranty = abap_true. + rv_price = rv_price * c_warranty_rate. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1312/reference/phone_repair_pricer.testclasses.abap b/tasks_gen/train/G1312/reference/phone_repair_pricer.testclasses.abap new file mode 100644 index 0000000..b4393e6 --- /dev/null +++ b/tasks_gen/train/G1312/reference/phone_repair_pricer.testclasses.abap @@ -0,0 +1,84 @@ +CLASS ltc_phone_repair_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_screen_basic TYPE ty_price VALUE '89.00'. + CONSTANTS c_port_high TYPE ty_price VALUE '61.25'. + CONSTANTS c_camera_express TYPE ty_price VALUE '74.00'. + CONSTANTS c_camera_full TYPE ty_price VALUE '59.20'. + CONSTANTS c_minimum TYPE ty_price VALUE '25.00'. + CONSTANTS c_zero TYPE ty_price VALUE '0.00'. + + DATA mo_cut TYPE REF TO {{p}}phone_repair_pricer. + + METHODS setup. + METHODS base_price_screen FOR TESTING. + METHODS device_factor_high FOR TESTING. + METHODS express_fee FOR TESTING. + METHODS warranty_rate FOR TESTING. + METHODS minimum_price FOR TESTING. + METHODS unknown_repair FOR TESTING. +ENDCLASS. + + +CLASS ltc_phone_repair_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}phone_repair_pricer( ). + ENDMETHOD. + + METHOD base_price_screen. + cl_abap_unit_assert=>assert_equals( + exp = c_screen_basic + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'SCREEN' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD device_factor_high. + cl_abap_unit_assert=>assert_equals( + exp = c_port_high + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'PORT' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD express_fee. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_express + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD warranty_rate. + cl_abap_unit_assert=>assert_equals( + exp = c_camera_full + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'CAMERA' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. + + METHOD minimum_price. + cl_abap_unit_assert=>assert_equals( + exp = c_minimum + act = mo_cut->calculate_price( iv_device = 'BASIC' + iv_repair = 'BATTERY' + iv_express = abap_false + iv_warranty = abap_false ) ). + ENDMETHOD. + + METHOD unknown_repair. + cl_abap_unit_assert=>assert_equals( + exp = c_zero + act = mo_cut->calculate_price( iv_device = 'HIGH' + iv_repair = 'SPEAKER' + iv_express = abap_true + iv_warranty = abap_true ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1312/seed/phone_repair_pricer.clas.abap b/tasks_gen/train/G1312/seed/phone_repair_pricer.clas.abap new file mode 100644 index 0000000..9efe690 --- /dev/null +++ b/tasks_gen/train/G1312/seed/phone_repair_pricer.clas.abap @@ -0,0 +1,90 @@ +CLASS {{p}}phone_repair_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_device TYPE c LENGTH 6. + TYPES ty_repair TYPE c LENGTH 8. + TYPES ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS c_device_basic TYPE ty_device VALUE 'BASIC'. + CONSTANTS c_device_mid TYPE ty_device VALUE 'MID'. + CONSTANTS c_device_high TYPE ty_device VALUE 'HIGH'. + + CONSTANTS c_repair_screen TYPE ty_repair VALUE 'SCREEN'. + CONSTANTS c_repair_battery TYPE ty_repair VALUE 'BATTERY'. + CONSTANTS c_repair_board TYPE ty_repair VALUE 'BOARD'. + CONSTANTS c_repair_camera TYPE ty_repair VALUE 'CAMERA'. + CONSTANTS c_repair_port TYPE ty_repair VALUE 'PORT'. + + METHODS calculate_price + IMPORTING iv_device TYPE ty_device + iv_repair TYPE ty_repair + iv_express TYPE abap_bool + iv_warranty TYPE abap_bool + RETURNING VALUE(rv_price) TYPE ty_price. +ENDCLASS. + + +CLASS {{p}}phone_repair_pricer IMPLEMENTATION. + METHOD calculate_price. + DATA lv_price TYPE ty_price. + DATA lv_factor TYPE ty_price. + DATA lv_found TYPE abap_bool. + + CLEAR lv_found. + IF iv_repair = c_repair_screen. + lv_price = '89.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_battery. + lv_price = '19.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_board. + lv_price = '149.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_camera. + lv_price = '59.00'. + lv_found = abap_true. + ELSEIF iv_repair = c_repair_port. + lv_price = '49.00'. + lv_found = abap_true. + ENDIF. + + IF lv_found = abap_true. + IF iv_device = c_device_high. + lv_factor = '1.25'. + lv_price = lv_price * lv_factor. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ELSEIF iv_device = c_device_mid. + lv_factor = '1.10'. + lv_price = lv_price * lv_factor. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ELSE. + IF iv_express = abap_true. + lv_price = lv_price + 15. + ENDIF. + IF iv_warranty = abap_true. + lv_factor = '0.80'. + lv_price = lv_price * lv_factor. + ENDIF. + ENDIF. + + IF lv_price < 25. + lv_price = 25. + ENDIF. + ELSE. + CLEAR lv_price. + ENDIF. + + rv_price = lv_price. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1312/spec.md b/tasks_gen/train/G1312/spec.md new file mode 100644 index 0000000..f8939c1 --- /dev/null +++ b/tasks_gen/train/G1312/spec.md @@ -0,0 +1,13 @@ +Hi, please refactor the class {{P}}PHONE_REPAIR_PRICER in package $TMP. The class prices repair orders for a mobile phone repair shop. The checkout program calls the class for each order and prints the price that the class returns. The class works, but its code is legacy code. It has one long method, repeated blocks, and prices that are hidden in the code. Refactor the class in place. The behavior must stay the same. + +There are no open questions. + +The class {{P}}PHONE_REPAIR_PRICER exists in package $TMP and is active. Its source code is the legacy code. The checkout program calls the public method CALCULATE_PRICE. It passes the device class, the repair type, the express flag, and the warranty flag. It prints the returned price. The class is in production. Its public interface must not change. The class has no tests. + +Refactor the class {{P}}PHONE_REPAIR_PRICER in package $TMP. Keep the name. Do not create a second class. The class stays public and final. The constructor stays public and has no parameters. The public section stays as it is. It has the type TY_DEVICE (character 6), the type TY_REPAIR (character 8), and the type TY_PRICE (packed number, length 8, 2 decimals). It has the constants C_DEVICE_BASIC, C_DEVICE_MID, and C_DEVICE_HIGH of type TY_DEVICE. It has the constants C_REPAIR_SCREEN, C_REPAIR_BATTERY, C_REPAIR_BOARD, C_REPAIR_CAMERA, and C_REPAIR_PORT of type TY_REPAIR. It has the method CALCULATE_PRICE with the importing parameters IV_DEVICE (TY_DEVICE), IV_REPAIR (TY_REPAIR), IV_EXPRESS (ABAP_BOOL), and IV_WARRANTY (ABAP_BOOL), and the returning parameter RV_PRICE (TY_PRICE). Do not add other public methods, types, constants, or attributes. + +The behavior of the class must not change. Every combination of the four input parameters must return the same price as before. The rules are in the legacy source code of the class. Read the legacy code. Keep every rule and keep the sequence of the steps. Do not add rules and do not remove rules. + +The release target is ABAP Platform 2025 (SAP_BASIS 816). Use Clean ABAP. Methods must be below 40 statements. Do not use global variables. Do not write comments that restate the code. Replace the prices and the factors with named constants. Remove the repeated blocks. Use names that say what the data and the methods mean. Do not change the public interface of the class. + +The class must be active and have no syntax error. The hidden tests must pass. Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1312/task.json b/tasks_gen/train/G1312/task.json new file mode 100644 index 0000000..9a8a5a3 --- /dev/null +++ b/tasks_gen/train/G1312/task.json @@ -0,0 +1,51 @@ +{ + "id": "G1312", + "category": "K", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}PHONE_REPAIR_PRICER", + "file": "seed/phone_repair_pricer.clas.abap", + "description": "Legacy phone repair price calculator: one long method, repeated blocks, prices hidden in the code" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PHONE_REPAIR_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "Behavior tests for the refactored phone repair price calculator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PHONE_REPAIR_PRICER", + "file": "reference/phone_repair_pricer.clas.abap", + "description": "Refactored phone repair price calculator", + "testclasses_file": "reference/phone_repair_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "duplicate_code", + "magic_numbers" + ], + "base_task": "G1010", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1313/generation.json b/tasks_gen/train/G1313/generation.json new file mode 100644 index 0000000..1fb2fd5 --- /dev/null +++ b/tasks_gen/train/G1313/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1313", + "pool": "train", + "category": "K", + "base_task": "G1031", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1313/hidden/t_cinema_hidden.clas.abap b/tasks_gen/train/G1313/hidden/t_cinema_hidden.clas.abap new file mode 100644 index 0000000..29c21de --- /dev/null +++ b/tasks_gen/train/G1313/hidden/t_cinema_hidden.clas.abap @@ -0,0 +1,76 @@ +CLASS {{p}}t_cinema_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_film FOR TESTING. + METHODS ticket_count FOR TESTING. + METHODS revenue FOR TESTING. + METHODS cancelled_excluded FOR TESTING. + METHODS different_films FOR TESTING. + METHODS no_tickets FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_cinema_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_FILM_REVENUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_screen TYPE STANDARD TABLE OF {{p}}screen WITH EMPTY KEY. + DATA lt_ticket TYPE STANDARD TABLE OF {{p}}ticket WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_screen = VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ( show_id = 'S3' film = 'F2' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S4' film = 'F3' show_date = '20250103' hall = 'H1' status = 'C' ) + ( show_id = 'S5' film = 'F4' show_date = '20250104' hall = 'H1' status = 'A' ) + ). + lt_ticket = VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S1' ticket_no = 2 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ( show_id = 'S3' ticket_no = 1 price = '8.00' ) + ( show_id = 'S4' ticket_no = 1 price = '9.00' ) + ). + go_env->insert_test_data( lt_screen ). + go_env->insert_test_data( lt_ticket ). + ENDMETHOD. + + METHOD one_row_per_film. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD ticket_count. + SELECT SINGLE ticket_count FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ). + ENDMETHOD. + + METHOD revenue. + SELECT SINGLE revenue FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '32.50' ) act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. + + METHOD cancelled_excluded. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F3' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD different_films. + SELECT COUNT(*) FROM {{p}}i_film_revenue INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. + + METHOD no_tickets. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1313/reference/i_film_revenue.ddls.asddls b/tasks_gen/train/G1313/reference/i_film_revenue.ddls.asddls new file mode 100644 index 0000000..1045f29 --- /dev/null +++ b/tasks_gen/train/G1313/reference/i_film_revenue.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.film diff --git a/tasks_gen/train/G1313/reference/t_cinema_test.clas.abap b/tasks_gen/train/G1313/reference/t_cinema_test.clas.abap new file mode 100644 index 0000000..0188aca --- /dev/null +++ b/tasks_gen/train/G1313/reference/t_cinema_test.clas.abap @@ -0,0 +1,46 @@ +CLASS {{p}}t_cinema_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_per_film FOR TESTING. + METHODS revenue FOR TESTING. +ENDCLASS. + +CLASS {{p}}t_cinema_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_FILM_REVENUE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_screen TYPE STANDARD TABLE OF {{p}}screen WITH EMPTY KEY. + DATA lt_ticket TYPE STANDARD TABLE OF {{p}}ticket WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_screen = VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ). + lt_ticket = VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ). + go_env->insert_test_data( lt_screen ). + go_env->insert_test_data( lt_ticket ). + ENDMETHOD. + + METHOD one_row_per_film. + SELECT COUNT(*) FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rows ). + ENDMETHOD. + + METHOD revenue. + SELECT SINGLE revenue FROM {{p}}i_film_revenue WHERE film = 'F1' INTO @DATA(lv_revenue). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '22.50' ) act = CONV decfloat34( lv_revenue ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1313/seed/i_film_revenue.ddls.asddls b/tasks_gen/train/G1313/seed/i_film_revenue.ddls.asddls new file mode 100644 index 0000000..756eb64 --- /dev/null +++ b/tasks_gen/train/G1313/seed/i_film_revenue.ddls.asddls @@ -0,0 +1,15 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Film revenue' +define view entity {{P}}I_FILM_REVENUE + as select from {{p}}screen as s + inner join {{p}}ticket as t on t.show_id = s.show_id +{ + key s.show_id as show_id, + s.film as film, + count( * ) as ticket_count, + sum( t.price ) as revenue +} +where s.status = 'A' +group by + s.show_id, + s.film diff --git a/tasks_gen/train/G1313/seed/screen.tabl.asabap b/tasks_gen/train/G1313/seed/screen.tabl.asabap new file mode 100644 index 0000000..1118ca2 --- /dev/null +++ b/tasks_gen/train/G1313/seed/screen.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Screening' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}screen { + key client : abap.clnt not null; + key show_id : abap.char(10) not null; + film : abap.char(10); + show_date : abap.dats; + hall : abap.char(4); + status : abap.char(1); +} diff --git a/tasks_gen/train/G1313/seed/seed_cinema.clas.abap b/tasks_gen/train/G1313/seed/seed_cinema.clas.abap new file mode 100644 index 0000000..f70b258 --- /dev/null +++ b/tasks_gen/train/G1313/seed/seed_cinema.clas.abap @@ -0,0 +1,28 @@ +CLASS {{p}}seed_cinema DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS {{p}}seed_cinema IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}screen. + DELETE FROM {{p}}ticket. + INSERT {{p}}screen FROM TABLE @( VALUE #( + ( show_id = 'S1' film = 'F1' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S2' film = 'F1' show_date = '20250102' hall = 'H2' status = 'A' ) + ( show_id = 'S3' film = 'F2' show_date = '20250101' hall = 'H1' status = 'A' ) + ( show_id = 'S4' film = 'F3' show_date = '20250103' hall = 'H1' status = 'C' ) + ( show_id = 'S5' film = 'F4' show_date = '20250104' hall = 'H1' status = 'A' ) + ) ). + INSERT {{p}}ticket FROM TABLE @( VALUE #( + ( show_id = 'S1' ticket_no = 1 price = '10.00' ) + ( show_id = 'S1' ticket_no = 2 price = '10.00' ) + ( show_id = 'S2' ticket_no = 1 price = '12.50' ) + ( show_id = 'S3' ticket_no = 1 price = '8.00' ) + ( show_id = 'S4' ticket_no = 1 price = '9.00' ) + ) ). + out->write( 'Seed data inserted.' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1313/seed/ticket.tabl.asabap b/tasks_gen/train/G1313/seed/ticket.tabl.asabap new file mode 100644 index 0000000..89fd489 --- /dev/null +++ b/tasks_gen/train/G1313/seed/ticket.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Ticket' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}ticket { + key client : abap.clnt not null; + key show_id : abap.char(10) not null; + key ticket_no : abap.int4 not null; + price : abap.dec(15,2); +} diff --git a/tasks_gen/train/G1313/spec.md b/tasks_gen/train/G1313/spec.md new file mode 100644 index 0000000..d637499 --- /dev/null +++ b/tasks_gen/train/G1313/spec.md @@ -0,0 +1 @@ +Fix the CDS view entity {{P}}I_FILM_REVENUE in package $TMP. It must show one row for each film that has at least one ticket. Do not show films without tickets. TICKET_COUNT is the number of tickets sold for the film. REVENUE is the sum of the prices of all tickets for the film. The view elements are FILM (key), TICKET_COUNT, and REVENUE. The view has no authorization check (#NOT_REQUIRED). All other behavior stays the same. Do not change {{P}}SCREEN, {{P}}TICKET, or {{P}}SEED_CINEMA. Do not create other objects. The target release is SAP_BASIS 816. Use Clean ABAP and have no ATC priority 1 or 2 findings. The view must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/train/G1313/task.json b/tasks_gen/train/G1313/task.json new file mode 100644 index 0000000..758d68d --- /dev/null +++ b/tasks_gen/train/G1313/task.json @@ -0,0 +1,80 @@ +{ + "id": "G1313", + "category": "K", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}SCREEN", + "file": "seed/screen.tabl.asabap", + "description": "Screening" + }, + { + "type": "TABL", + "name": "{{P}}TICKET", + "file": "seed/ticket.tabl.asabap", + "description": "Ticket" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_CINEMA", + "file": "seed/seed_cinema.clas.abap", + "description": "Seed data", + "run": true + }, + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "file": "seed/i_film_revenue.ddls.asddls", + "description": "Faulty view" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "fields": [ + "FILM", + "TICKET_COUNT", + "REVENUE" + ] + } + ], + "out_of_scope": [ + "{{P}}SCREEN", + "{{P}}TICKET", + "{{P}}SEED_CINEMA" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_CINEMA_HIDDEN", + "file": "hidden/t_cinema_hidden.clas.abap", + "description": "Hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_FILM_REVENUE", + "file": "reference/i_film_revenue.ddls.asddls", + "description": "Fixed view" + }, + { + "type": "CLAS", + "name": "{{P}}T_CINEMA_TEST", + "file": "reference/t_cinema_test.clas.abap", + "description": "Own tests" + } + ], + "craft_checks": [], + "base_task": "G1031", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1314/generation.json b/tasks_gen/train/G1314/generation.json new file mode 100644 index 0000000..9c5ec95 --- /dev/null +++ b/tasks_gen/train/G1314/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1314", + "pool": "train", + "category": "K", + "base_task": "G1026", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1314/hidden/t88_hidden.clas.abap b/tasks_gen/train/G1314/hidden/t88_hidden.clas.abap new file mode 100644 index 0000000..84e8951 --- /dev/null +++ b/tasks_gen/train/G1314/hidden/t88_hidden.clas.abap @@ -0,0 +1,129 @@ +CLASS {{p}}t88_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call + IMPORTING iv_type TYPE string + iv_hours TYPE decfloat34 + iv_fee TYPE decfloat34 DEFAULT 7 + EXPORTING ev_skip TYPE abap_bool + ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS day_shift_fee FOR TESTING. + METHODS day_rounding FOR TESTING. + METHODS day_minimum_fee FOR TESTING. + METHODS night_shift_fee FOR TESTING. + METHODS night_rounding FOR TESTING. + METHODS holiday_shift_fee FOR TESTING. + METHODS holiday_above_min FOR TESTING. + METHODS other_shift_zero FOR TESTING. + METHODS zero_hours_rejected FOR TESTING. + METHODS negative_hours_rejected FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t88_hidden IMPLEMENTATION. + METHOD call. + ev_fee = iv_fee. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = iv_type + iv_hours = iv_hours + IMPORTING + ev_surcharge = ev_skip + CHANGING + cv_fee = ev_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD day_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '102.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD day_rounding. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '57.38'. + call( EXPORTING iv_type = 'DAY' iv_hours = '2.25' + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD day_minimum_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '50.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 1 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD night_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '127.50'. + call( EXPORTING iv_type = 'NIGHT' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + ENDMETHOD. + + METHOD night_rounding. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '71.72'. + call( EXPORTING iv_type = 'NIGHT' iv_hours = '2.25' + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD holiday_shift_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '204.00'. + call( EXPORTING iv_type = 'HOLIDAY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_skip ). + ENDMETHOD. + + METHOD holiday_above_min. + DATA lv_fee TYPE decfloat34. + DATA lv_exp TYPE decfloat34 VALUE '51.00'. + call( EXPORTING iv_type = 'HOLIDAY' iv_hours = 1 + IMPORTING ev_fee = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD other_shift_zero. + DATA lv_fee TYPE decfloat34. + DATA lv_skip TYPE abap_bool. + DATA lv_exp TYPE decfloat34 VALUE '0.00'. + call( EXPORTING iv_type = 'STANDBY' iv_hours = 4 + IMPORTING ev_fee = lv_fee ev_skip = lv_skip ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + cl_abap_unit_assert=>assert_equals( exp = abap_false act = lv_skip ). + ENDMETHOD. + + METHOD zero_hours_rejected. + DATA lv_fee TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + DATA lv_exp TYPE decfloat34 VALUE '7.00'. + call( EXPORTING iv_type = 'DAY' iv_hours = 0 iv_fee = 7 + IMPORTING ev_fee = lv_fee ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = lv_fee ). + ENDMETHOD. + + METHOD negative_hours_rejected. + DATA lv_rc TYPE sysubrc. + call( EXPORTING iv_type = 'NIGHT' iv_hours = '-2.5' + IMPORTING ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1314/reference/guard_fee.func.abap b/tasks_gen/train/G1314/reference/guard_fee.func.abap new file mode 100644 index 0000000..7a765bd --- /dev/null +++ b/tasks_gen/train/G1314/reference/guard_fee.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}guard_fee + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + CONSTANTS c_base_rate TYPE decfloat34 VALUE '25.50'. + CONSTANTS c_night_factor TYPE decfloat34 VALUE '1.25'. + CONSTANTS c_holiday_factor TYPE decfloat34 VALUE '2.00'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '50.00'. + + DATA lv_known TYPE abap_bool. + + ev_surcharge = abap_false. + + IF iv_hours <= 0. + RAISE invalid_hours. + ENDIF. + + CASE iv_shift_type. + WHEN 'DAY'. + lv_known = abap_true. + cv_fee = round( val = iv_hours * c_base_rate dec = 2 ). + WHEN 'NIGHT'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_night_factor dec = 2 ). + WHEN 'HOLIDAY'. + lv_known = abap_true. + ev_surcharge = abap_true. + cv_fee = round( val = iv_hours * c_base_rate * c_holiday_factor dec = 2 ). + WHEN OTHERS. + cv_fee = 0. + ENDCASE. + + IF lv_known = abap_true AND cv_fee < c_min_fee. + cv_fee = c_min_fee. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1314/reference/t88_test.clas.abap b/tasks_gen/train/G1314/reference/t88_test.clas.abap new file mode 100644 index 0000000..dd21afb --- /dev/null +++ b/tasks_gen/train/G1314/reference/t88_test.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}t88_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_type TYPE string + iv_hours TYPE decfloat34 + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS day_fee FOR TESTING. + METHODS night_fee FOR TESTING. + METHODS holiday_fee FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS invalid_hours FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t88_test IMPLEMENTATION. + METHOD fee_for. + DATA lv_skip TYPE abap_bool. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = iv_type + iv_hours = iv_hours + IMPORTING + ev_surcharge = lv_skip + CHANGING + cv_fee = rv_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 0 act = sy-subrc ). + ENDMETHOD. + + METHOD day_fee. + DATA lv_exp TYPE decfloat34 VALUE '102.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'DAY' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD night_fee. + DATA lv_exp TYPE decfloat34 VALUE '127.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'NIGHT' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD holiday_fee. + DATA lv_exp TYPE decfloat34 VALUE '204.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'HOLIDAY' iv_hours = 4 ) ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_exp TYPE decfloat34 VALUE '50.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_exp act = fee_for( iv_type = 'DAY' iv_hours = 1 ) ). + ENDMETHOD. + + METHOD invalid_hours. + DATA lv_fee TYPE decfloat34 VALUE '7.00'. + DATA lv_expected TYPE decfloat34 VALUE '7.00'. + CALL FUNCTION '{{P}}GUARD_FEE' + EXPORTING + iv_shift_type = 'DAY' + iv_hours = 0 + CHANGING + cv_fee = lv_fee + EXCEPTIONS + invalid_hours = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lv_fee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1314/seed/guard_sample.func.abap b/tasks_gen/train/G1314/seed/guard_sample.func.abap new file mode 100644 index 0000000..ad4ee7f --- /dev/null +++ b/tasks_gen/train/G1314/seed/guard_sample.func.abap @@ -0,0 +1,14 @@ +FUNCTION {{p}}guard_sample + IMPORTING + iv_shift_type TYPE string + iv_hours TYPE decfloat34 + EXPORTING + ev_surcharge TYPE abap_bool + CHANGING + cv_fee TYPE decfloat34 + EXCEPTIONS + invalid_hours. + + ev_surcharge = abap_false. + +ENDFUNCTION. diff --git a/tasks_gen/train/G1314/spec.md b/tasks_gen/train/G1314/spec.md new file mode 100644 index 0000000..1fdf652 --- /dev/null +++ b/tasks_gen/train/G1314/spec.md @@ -0,0 +1,5 @@ +Please create the function module {{P}}GUARD_FEE in the function group {{P}}FG_GUARD in package $TMP. The function group {{P}}FG_GUARD already exists. The function module {{P}}GUARD_SAMPLE exists in this function group. It is the sample module. The billing program calls all shift modules with the same interface. Use exactly the interface of {{P}}GUARD_SAMPLE: the same parameter names, parameter kinds, types, and exceptions. Do not change {{P}}GUARD_SAMPLE. Do not use application tables. Do not use transports. The release target is 7.02. Use only syntax that release 7.02 supports. There are no open questions. + +The function module calculates the fee for a guard shift. The parameters are iv_shift_type, iv_hours, ev_surcharge, and cv_fee. If iv_hours is less than or equal to 0, raise the exception INVALID_HOURS. Do not change cv_fee. Set ev_surcharge to false. If iv_shift_type is DAY, set cv_fee to iv_hours multiplied by 25.50. If iv_shift_type is NIGHT, set cv_fee to iv_hours multiplied by 25.50 multiplied by 1.25. Set ev_surcharge to true. If iv_shift_type is HOLIDAY, set cv_fee to iv_hours multiplied by 25.50 multiplied by 2.00. Set ev_surcharge to true. For any other shift type, set cv_fee to 0. Do not apply the rounding and minimum rules for any other shift type. For iv_shift_type DAY, NIGHT, and HOLIDAY, round cv_fee half up to 2 decimal places. For these three shift types, if cv_fee is less than 50.00, set cv_fee to 50.00. + +The function module {{P}}GUARD_FEE must be active and have no syntax error. The hidden tests must pass. Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/train/G1314/task.json b/tasks_gen/train/G1314/task.json new file mode 100644 index 0000000..c566aeb --- /dev/null +++ b/tasks_gen/train/G1314/task.json @@ -0,0 +1,83 @@ +{ + "id": "G1314", + "category": "K", + "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_GUARD", + "description": "Guard shift function group" + }, + { + "type": "FUNC", + "name": "{{P}}GUARD_SAMPLE", + "functionGroup": "{{P}}FG_GUARD", + "file": "seed/guard_sample.func.abap", + "description": "Sample shift module that fixes the interface" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}GUARD_FEE", + "functionGroup": "{{P}}FG_GUARD", + "params": [ + { + "name": "iv_shift_type", + "type": "string" + }, + { + "name": "iv_hours", + "type": "decfloat34" + }, + { + "name": "ev_surcharge", + "type": "abap_bool" + }, + { + "name": "cv_fee", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}GUARD_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T88_HIDDEN", + "file": "hidden/t88_hidden.clas.abap", + "description": "T88 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}GUARD_FEE", + "functionGroup": "{{P}}FG_GUARD", + "file": "reference/guard_fee.func.abap", + "description": "Guard shift fee module" + }, + { + "type": "CLAS", + "name": "{{P}}T88_TEST", + "file": "reference/t88_test.clas.abap", + "description": "T88 own tests" + } + ], + "craft_checks": [ + "method_length", + "release_syntax" + ], + "base_task": "G1026", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1315/generation.json b/tasks_gen/train/G1315/generation.json new file mode 100644 index 0000000..ab24936 --- /dev/null +++ b/tasks_gen/train/G1315/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1315", + "pool": "train", + "category": "K", + "base_task": "G1013", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1315/hidden/t20_hidden.clas.abap b/tasks_gen/train/G1315/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..c98537f --- /dev/null +++ b/tasks_gen/train/G1315/hidden/t20_hidden.clas.abap @@ -0,0 +1,138 @@ +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}}log_merger. + DATA mt_empty TYPE {{p}}log_merger=>tt_log. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS first_wins_on_duplicate FOR TESTING. + METHODS first_wins_within_list FOR TESTING. + METHODS sorts_by_log_no FOR TESTING. + METHODS drops_short_logs FOR TESTING. + METHODS drops_thin_logs FOR TESTING. + METHODS filter_after_dedup FOR TESTING. + METHODS empty_inputs FOR TESTING. + METHODS one_empty_list FOR TESTING. + METHODS counts_by_district FOR TESTING. + METHODS counts_empty_list FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}log_merger( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 600 diameter_cm = 40 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_result[ 2 ]-district ). + ENDMETHOD. + + METHOD first_wins_on_duplicate. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = lt_result[ 1 ]-diameter_cm ). + ENDMETHOD. + + METHOD first_wins_within_list. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000001' district = 'A002' length_cm = 700 diameter_cm = 40 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 500 act = lt_result[ 1 ]-length_cm ). + ENDMETHOD. + + METHOD sorts_by_log_no. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000003' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 500 diameter_cm = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000003' act = lt_result[ 3 ]-log_no ). + ENDMETHOD. + + METHOD drops_short_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 199 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 200 diameter_cm = 30 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 1 ]-log_no ). + ENDMETHOD. + + METHOD drops_thin_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 19 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 20 ) ) + it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 1 ]-log_no ). + ENDMETHOD. + + METHOD filter_after_dedup. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 100 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD empty_inputs. + DATA(lt_result) = mo_cut->combine( it_first = mt_empty it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD one_empty_list. + DATA(lt_result) = mo_cut->combine( + it_first = mt_empty + it_second = VALUE #( ( log_no = 'LOG0000009' district = 'B001' length_cm = 800 diameter_cm = 45 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000009' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_result[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 800 act = lt_result[ 1 ]-length_cm ). + ENDMETHOD. + + METHOD counts_by_district. + DATA(lt_stat) = mo_cut->count_by_district( + it_log = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'B001' length_cm = 300 diameter_cm = 25 ) + ( log_no = 'LOG0000003' district = 'A001' length_cm = 200 diameter_cm = 20 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_stat ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_stat[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_stat[ 1 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_stat[ 1 ]-total_length_cm ). + cl_abap_unit_assert=>assert_equals( exp = 'B001' act = lt_stat[ 2 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_stat[ 2 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_stat[ 2 ]-total_length_cm ). + ENDMETHOD. + + METHOD counts_empty_list. + DATA(lt_stat) = mo_cut->count_by_district( it_log = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_stat ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1315/reference/log_merger.clas.abap b/tasks_gen/train/G1315/reference/log_merger.clas.abap new file mode 100644 index 0000000..569cec6 --- /dev/null +++ b/tasks_gen/train/G1315/reference/log_merger.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}log_merger DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_log_no TYPE c LENGTH 10, + ty_district TYPE c LENGTH 4, + BEGIN OF ty_log, + log_no TYPE ty_log_no, + district TYPE ty_district, + length_cm TYPE i, + diameter_cm TYPE i, + END OF ty_log, + tt_log TYPE STANDARD TABLE OF ty_log WITH EMPTY KEY, + BEGIN OF ty_district_stat, + district TYPE ty_district, + log_qty TYPE i, + total_length_cm TYPE i, + END OF ty_district_stat, + tt_district_stat TYPE STANDARD TABLE OF ty_district_stat WITH EMPTY KEY. + + METHODS combine + IMPORTING it_first TYPE tt_log + it_second TYPE tt_log + RETURNING VALUE(rt_result) TYPE tt_log. + + METHODS count_by_district + IMPORTING it_log TYPE tt_log + RETURNING VALUE(rt_stat) TYPE tt_district_stat. + + PRIVATE SECTION. + CONSTANTS: + c_min_length TYPE i VALUE 200, + c_min_diameter TYPE i VALUE 20. + + TYPES tt_log_keyed TYPE SORTED TABLE OF ty_log WITH UNIQUE KEY log_no. + + METHODS add_logs + IMPORTING it_log TYPE tt_log + CHANGING ct_keyed TYPE tt_log_keyed. + + METHODS is_ready + IMPORTING is_log TYPE ty_log + RETURNING VALUE(rv_ready) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}log_merger IMPLEMENTATION. + METHOD combine. + DATA lt_keyed TYPE tt_log_keyed. + add_logs( EXPORTING it_log = it_first CHANGING ct_keyed = lt_keyed ). + add_logs( EXPORTING it_log = it_second CHANGING ct_keyed = lt_keyed ). + LOOP AT lt_keyed INTO DATA(ls_log). + IF is_ready( ls_log ) = abap_true. + INSERT ls_log INTO TABLE rt_result. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD count_by_district. + DATA lt_stat TYPE SORTED TABLE OF ty_district_stat WITH UNIQUE KEY district. + LOOP AT it_log INTO DATA(ls_log). + ASSIGN lt_stat[ district = ls_log-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_log-district + log_qty = 1 + total_length_cm = ls_log-length_cm ) INTO TABLE lt_stat. + ELSE. + -log_qty += 1. + -total_length_cm += ls_log-length_cm. + ENDIF. + ENDLOOP. + rt_stat = lt_stat. + ENDMETHOD. + + METHOD add_logs. + LOOP AT it_log INTO DATA(ls_log). + INSERT ls_log INTO TABLE ct_keyed. + ENDLOOP. + ENDMETHOD. + + METHOD is_ready. + rv_ready = xsdbool( is_log-length_cm >= c_min_length + AND is_log-diameter_cm >= c_min_diameter ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1315/reference/log_merger.testclasses.abap b/tasks_gen/train/G1315/reference/log_merger.testclasses.abap new file mode 100644 index 0000000..4559836 --- /dev/null +++ b/tasks_gen/train/G1315/reference/log_merger.testclasses.abap @@ -0,0 +1,71 @@ +CLASS ltc_log_merger DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}log_merger. + DATA mt_empty TYPE {{p}}log_merger=>tt_log. + METHODS setup. + METHODS merges_distinct_logs FOR TESTING. + METHODS keeps_first_duplicate FOR TESTING. + METHODS removes_small_logs FOR TESTING. + METHODS sorts_result FOR TESTING. + METHODS empty_input_is_empty FOR TESTING. + METHODS counts_per_district FOR TESTING. +ENDCLASS. + + +CLASS ltc_log_merger IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}log_merger( ). + ENDMETHOD. + + METHOD merges_distinct_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'B001' length_cm = 600 diameter_cm = 40 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + ENDMETHOD. + + METHOD keeps_first_duplicate. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 900 diameter_cm = 50 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'A001' act = lt_result[ 1 ]-district ). + ENDMETHOD. + + METHOD removes_small_logs. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 150 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 15 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD sorts_result. + DATA(lt_result) = mo_cut->combine( + it_first = VALUE #( ( log_no = 'LOG0000002' district = 'A001' length_cm = 500 diameter_cm = 30 ) ) + it_second = VALUE #( ( log_no = 'LOG0000001' district = 'B001' length_cm = 500 diameter_cm = 30 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000001' act = lt_result[ 1 ]-log_no ). + cl_abap_unit_assert=>assert_equals( exp = 'LOG0000002' act = lt_result[ 2 ]-log_no ). + ENDMETHOD. + + METHOD empty_input_is_empty. + DATA(lt_result) = mo_cut->combine( it_first = mt_empty it_second = mt_empty ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD counts_per_district. + DATA(lt_stat) = mo_cut->count_by_district( + it_log = VALUE #( ( log_no = 'LOG0000001' district = 'A001' length_cm = 500 diameter_cm = 30 ) + ( log_no = 'LOG0000002' district = 'A001' length_cm = 300 diameter_cm = 25 ) ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_stat ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_stat[ 1 ]-log_qty ). + cl_abap_unit_assert=>assert_equals( exp = 800 act = lt_stat[ 1 ]-total_length_cm ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1315/spec.md b/tasks_gen/train/G1315/spec.md new file mode 100644 index 0000000..0713521 --- /dev/null +++ b/tasks_gen/train/G1315/spec.md @@ -0,0 +1,7 @@ +Create the public final class {{P}}LOG_MERGER in package $TMP with a public constructor without parameters. Define public types TY_LOG_NO (char10), TY_DISTRICT (char4), TY_LOG with components LOG_NO (TY_LOG_NO), DISTRICT (TY_DISTRICT), LENGTH_CM (integer), DIAMETER_CM (integer), TT_LOG (standard table of TY_LOG with empty key), TY_DISTRICT_STAT with components DISTRICT (TY_DISTRICT), LOG_QTY (integer), TOTAL_LENGTH_CM (integer), and TT_DISTRICT_STAT (standard table of TY_DISTRICT_STAT with empty key). Define public methods COMBINE importing IT_FIRST (TT_LOG) and IT_SECOND (TT_LOG) returning RT_RESULT (TT_LOG), and COUNT_BY_DISTRICT importing IT_LOG (TT_LOG) returning RT_STAT (TT_DISTRICT_STAT). Do not add other public methods. + +COMBINE must merge IT_FIRST and IT_SECOND. Read IT_FIRST first, then IT_SECOND. Add a log only if its log number is not yet in the merged list. Ignore every later log with the same log number. Remove every log with LENGTH_CM below 200 or DIAMETER_CM below 20. Sort the result by LOG_NO ascending. The result must have no two logs with the same log number. + +COUNT_BY_DISTRICT must return one row per district in IT_LOG. LOG_QTY is the number of logs of the district. TOTAL_LENGTH_CM is the sum of LENGTH_CM of the logs of the district. Sort the result by DISTRICT ascending. An empty input gives an empty result. + +Use ABAP Platform 2025 (SAP_BASIS 816). Follow Clean ABAP: methods below 40 statements, no global variables, no comments that restate the code. Do not create other objects. Write ABAP Unit tests for the class. The class must be active and have no syntax error. diff --git a/tasks_gen/train/G1315/task.json b/tasks_gen/train/G1315/task.json new file mode 100644 index 0000000..0d60e5c --- /dev/null +++ b/tasks_gen/train/G1315/task.json @@ -0,0 +1,44 @@ +{ + "id": "G1315", + "category": "K", + "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}}LOG_MERGER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "Hidden tests for {{P}}LOG_MERGER" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}LOG_MERGER", + "file": "reference/log_merger.clas.abap", + "description": "Log merger that combines two log lists with keyed internal tables", + "testclasses_file": "reference/log_merger.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops", + "keyed_table_access" + ], + "base_task": "G1013", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/G1316/generation.json b/tasks_gen/train/G1316/generation.json new file mode 100644 index 0000000..9e0b9e5 --- /dev/null +++ b/tasks_gen/train/G1316/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1316", + "pool": "train", + "category": "K", + "base_task": "G1014", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1316/hidden/frame_pricer_hidden.clas.abap b/tasks_gen/train/G1316/hidden/frame_pricer_hidden.clas.abap new file mode 100644 index 0000000..f67db01 --- /dev/null +++ b/tasks_gen/train/G1316/hidden/frame_pricer_hidden.clas.abap @@ -0,0 +1,171 @@ +CLASS {{p}}frame_pricer_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + CONSTANTS: + c_exp_standard TYPE ty_amount VALUE '59.20', + c_exp_premium TYPE ty_amount VALUE '155.00', + c_exp_gold TYPE ty_amount VALUE '271.00', + c_exp_small TYPE ty_amount VALUE '37.84', + c_exp_perimeter TYPE ty_amount VALUE '34.00', + c_exp_oversized TYPE ty_amount VALUE '119.40', + c_exp_area TYPE ty_amount VALUE '96.00', + c_exp_qty_five TYPE ty_amount VALUE '115.00', + c_exp_qty_four TYPE ty_amount VALUE '107.00', + c_exp_zero TYPE ty_amount VALUE '0.00'. + DATA mo_cut TYPE REF TO {{p}}frame_pricer. + METHODS setup. + METHODS standard_frame FOR TESTING. + METHODS premium_frame FOR TESTING. + METHODS gold_frame_no_glass FOR TESTING. + METHODS small_frame_surcharge FOR TESTING. + METHODS perimeter_at_limit FOR TESTING. + METHODS oversized_surcharge FOR TESTING. + METHODS area_at_limit FOR TESTING. + METHODS quantity_five_no_setup FOR TESTING. + METHODS quantity_four_setup FOR TESTING. + METHODS invalid_dimensions FOR TESTING. + METHODS invalid_moulding FOR TESTING. + METHODS invalid_glass FOR TESTING. +ENDCLASS. + + +CLASS {{p}}frame_pricer_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}frame_pricer( ). + ENDMETHOD. + + METHOD standard_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_standard + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD premium_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_premium + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'P' + iv_glass = 'A' + iv_quantity = 2 ) ). + ENDMETHOD. + + METHOD gold_frame_no_glass. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_gold + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'G' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD small_frame_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_small + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 14 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD perimeter_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_perimeter + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 15 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD oversized_surcharge. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_oversized + act = mo_cut->calculate_price( iv_width_cm = 150 + iv_height_cm = 140 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD area_at_limit. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_area + act = mo_cut->calculate_price( iv_width_cm = 100 + iv_height_cm = 200 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD quantity_five_no_setup. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_qty_five + act = mo_cut->calculate_price( iv_width_cm = 20 + iv_height_cm = 30 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD quantity_four_setup. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_qty_four + act = mo_cut->calculate_price( iv_width_cm = 20 + iv_height_cm = 30 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 4 ) ). + ENDMETHOD. + + METHOD invalid_dimensions. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 0 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) msg = `width` ). + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 0 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) msg = `height` ). + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 0 ) msg = `quantity` ). + ENDMETHOD. + + METHOD invalid_moulding. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'X' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD invalid_glass. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'X' + iv_quantity = 1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1316/reference/frame_pricer.clas.abap b/tasks_gen/train/G1316/reference/frame_pricer.clas.abap new file mode 100644 index 0000000..ffa43ca --- /dev/null +++ b/tasks_gen/train/G1316/reference/frame_pricer.clas.abap @@ -0,0 +1,189 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + BEGIN OF c_moulding, + standard TYPE ty_moulding VALUE 'S', + premium TYPE ty_moulding VALUE 'P', + gold TYPE ty_moulding VALUE 'G', + END OF c_moulding, + BEGIN OF c_glass, + none TYPE ty_glass VALUE 'N', + standard TYPE ty_glass VALUE 'S', + anti_reflex TYPE ty_glass VALUE 'A', + END OF c_glass, + BEGIN OF c_moulding_rate, + standard TYPE ty_amount VALUE '0.08', + premium TYPE ty_amount VALUE '0.15', + gold TYPE ty_amount VALUE '0.28', + END OF c_moulding_rate, + BEGIN OF c_glass_price, + none TYPE ty_amount VALUE '0.00', + standard TYPE ty_amount VALUE '18.00', + anti_reflex TYPE ty_amount VALUE '34.00', + END OF c_glass_price, + c_factor TYPE ty_amount VALUE '2.00', + c_backing TYPE ty_amount VALUE '3.00', + c_labour TYPE ty_amount VALUE '12.00', + c_small_frame TYPE ty_amount VALUE '4.00', + c_small_limit_cm TYPE ty_amount VALUE '50.00', + c_oversized TYPE ty_amount VALUE '25.00', + c_oversize_limit TYPE ty_amount VALUE '20000.00', + c_setup_fee TYPE ty_amount VALUE '15.00', + c_setup_limit TYPE i VALUE 5. + + METHODS is_valid + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS perimeter + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_perimeter) TYPE ty_amount. + METHODS area + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + RETURNING VALUE(rv_area) TYPE ty_amount. + METHODS quantity_amount + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_amount) TYPE ty_amount. + METHODS moulding_price + IMPORTING iv_moulding TYPE ty_moulding + iv_perimeter TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS glass_price + IMPORTING iv_glass TYPE ty_glass + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS extras + IMPORTING iv_perimeter TYPE ty_amount + iv_area TYPE ty_amount + RETURNING VALUE(rv_price) TYPE ty_amount. + METHODS setup_fee + IMPORTING iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + IF is_valid( iv_width_cm = iv_width_cm + iv_height_cm = iv_height_cm + iv_moulding = iv_moulding + iv_glass = iv_glass + iv_quantity = iv_quantity ) = abap_false. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_perimeter TYPE ty_amount. + lv_perimeter = perimeter( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + DATA lv_area TYPE ty_amount. + lv_area = area( iv_width_cm = iv_width_cm iv_height_cm = iv_height_cm ). + + DATA lv_per_frame TYPE ty_amount. + lv_per_frame = moulding_price( iv_moulding = iv_moulding + iv_perimeter = lv_perimeter ). + DATA lv_glass TYPE ty_amount. + lv_glass = glass_price( iv_glass = iv_glass ). + lv_per_frame = lv_per_frame + lv_glass. + DATA lv_extra TYPE ty_amount. + lv_extra = extras( iv_perimeter = lv_perimeter iv_area = lv_area ). + lv_per_frame = lv_per_frame + lv_extra. + + DATA lv_quantity TYPE ty_amount. + lv_quantity = quantity_amount( iv_quantity = iv_quantity ). + DATA lv_total TYPE ty_amount. + lv_total = lv_per_frame * lv_quantity. + + DATA lv_fee TYPE ty_amount. + lv_fee = setup_fee( iv_quantity = iv_quantity ). + rv_price = lv_total + lv_fee. + ENDMETHOD. + + METHOD is_valid. + rv_valid = xsdbool( iv_width_cm >= 1 + AND iv_height_cm >= 1 + AND iv_quantity >= 1 + AND ( iv_moulding = c_moulding-standard + OR iv_moulding = c_moulding-premium + OR iv_moulding = c_moulding-gold ) + AND ( iv_glass = c_glass-none + OR iv_glass = c_glass-standard + OR iv_glass = c_glass-anti_reflex ) ). + ENDMETHOD. + + METHOD perimeter. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_perimeter = ( lv_width + lv_height ) * c_factor. + ENDMETHOD. + + METHOD area. + DATA lv_width TYPE ty_amount. + lv_width = iv_width_cm. + DATA lv_height TYPE ty_amount. + lv_height = iv_height_cm. + rv_area = lv_width * lv_height. + ENDMETHOD. + + METHOD quantity_amount. + rv_amount = iv_quantity. + ENDMETHOD. + + METHOD moulding_price. + CASE iv_moulding. + WHEN c_moulding-standard. + rv_price = iv_perimeter * c_moulding_rate-standard. + WHEN c_moulding-premium. + rv_price = iv_perimeter * c_moulding_rate-premium. + WHEN OTHERS. + rv_price = iv_perimeter * c_moulding_rate-gold. + ENDCASE. + ENDMETHOD. + + METHOD glass_price. + CASE iv_glass. + WHEN c_glass-none. + rv_price = c_glass_price-none. + WHEN c_glass-standard. + rv_price = c_glass_price-standard. + WHEN OTHERS. + rv_price = c_glass_price-anti_reflex. + ENDCASE. + ENDMETHOD. + + METHOD extras. + rv_price = c_backing + c_labour. + IF iv_perimeter < c_small_limit_cm. + rv_price = rv_price + c_small_frame. + ENDIF. + IF iv_area > c_oversize_limit. + rv_price = rv_price + c_oversized. + ENDIF. + ENDMETHOD. + + METHOD setup_fee. + IF iv_quantity < c_setup_limit. + rv_price = c_setup_fee. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1316/reference/frame_pricer.testclasses.abap b/tasks_gen/train/G1316/reference/frame_pricer.testclasses.abap new file mode 100644 index 0000000..ff27bed --- /dev/null +++ b/tasks_gen/train/G1316/reference/frame_pricer.testclasses.abap @@ -0,0 +1,87 @@ +CLASS ltc_frame_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + CONSTANTS: + c_exp_standard TYPE ty_amount VALUE '59.20', + c_exp_premium TYPE ty_amount VALUE '155.00', + c_exp_small TYPE ty_amount VALUE '37.84', + c_exp_oversized TYPE ty_amount VALUE '119.40', + c_exp_gold TYPE ty_amount VALUE '271.00', + c_exp_zero TYPE ty_amount VALUE '0.00'. + DATA mo_cut TYPE REF TO {{p}}frame_pricer. + METHODS setup. + METHODS standard_order FOR TESTING. + METHODS premium_order FOR TESTING. + METHODS small_frame FOR TESTING. + METHODS oversized_frame FOR TESTING. + METHODS no_setup_fee_for_five FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS ltc_frame_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}frame_pricer( ). + ENDMETHOD. + + METHOD standard_order. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_standard + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD premium_order. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_premium + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'P' + iv_glass = 'A' + iv_quantity = 2 ) ). + ENDMETHOD. + + METHOD small_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_small + act = mo_cut->calculate_price( iv_width_cm = 10 + iv_height_cm = 14 + iv_moulding = 'S' + iv_glass = 'N' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD oversized_frame. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_oversized + act = mo_cut->calculate_price( iv_width_cm = 150 + iv_height_cm = 140 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. + + METHOD no_setup_fee_for_five. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_gold + act = mo_cut->calculate_price( iv_width_cm = 30 + iv_height_cm = 40 + iv_moulding = 'G' + iv_glass = 'N' + iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD invalid_input. + cl_abap_unit_assert=>assert_equals( + exp = c_exp_zero + act = mo_cut->calculate_price( iv_width_cm = 0 + iv_height_cm = 40 + iv_moulding = 'S' + iv_glass = 'S' + iv_quantity = 1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1316/seed/frame_pricer.clas.abap b/tasks_gen/train/G1316/seed/frame_pricer.clas.abap new file mode 100644 index 0000000..0c4768b --- /dev/null +++ b/tasks_gen/train/G1316/seed/frame_pricer.clas.abap @@ -0,0 +1,127 @@ +CLASS {{p}}frame_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_moulding TYPE c LENGTH 1. + TYPES ty_glass TYPE c LENGTH 1. + TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2. + + METHODS calculate_price + IMPORTING iv_width_cm TYPE i + iv_height_cm TYPE i + iv_moulding TYPE ty_moulding + iv_glass TYPE ty_glass + iv_quantity TYPE i + RETURNING VALUE(rv_price) TYPE ty_amount. + + PRIVATE SECTION. + CONSTANTS: + c_rate_standard TYPE ty_amount VALUE '0.08', + c_rate_premium TYPE ty_amount VALUE '0.15', + c_rate_gold TYPE ty_amount VALUE '0.28'. + + DATA gv_width TYPE ty_amount. + DATA gv_height TYPE ty_amount. + DATA gv_perimeter TYPE ty_amount. + DATA gv_area TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}frame_pricer IMPLEMENTATION. + + METHOD calculate_price. + DATA lv_frame TYPE ty_amount. + DATA lv_glass TYPE ty_amount. + DATA lv_extra TYPE ty_amount. + DATA lv_sum TYPE ty_amount. + DATA lv_qty TYPE ty_amount. + DATA lv_factor TYPE ty_amount. + DATA lv_base TYPE ty_amount. + DATA lv_labour TYPE ty_amount. + DATA lv_small TYPE ty_amount. + DATA lv_big TYPE ty_amount. + DATA lv_setup TYPE ty_amount. + DATA lv_limit_s TYPE ty_amount. + DATA lv_limit_b TYPE ty_amount. + + CLEAR rv_price. + +* check the input data + IF iv_width_cm LT 1 OR iv_height_cm LT 1. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_quantity LT 1. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_moulding NE 'S' AND iv_moulding NE 'P' AND iv_moulding NE 'G'. + rv_price = 0. + RETURN. + ENDIF. + + IF iv_glass NE 'N' AND iv_glass NE 'S' AND iv_glass NE 'A'. + rv_price = 0. + RETURN. + ENDIF. + + MOVE iv_width_cm TO gv_width. + MOVE iv_height_cm TO gv_height. + MOVE 2 TO lv_factor. + COMPUTE gv_perimeter = ( gv_width + gv_height ) * lv_factor. + COMPUTE gv_area = gv_width * gv_height. + +* the price of the moulding + IF iv_moulding = 'S'. + COMPUTE lv_frame = gv_perimeter * c_rate_standard. + ELSE. + IF iv_moulding = 'P'. + COMPUTE lv_frame = gv_perimeter * c_rate_premium. + ELSE. + COMPUTE lv_frame = gv_perimeter * c_rate_gold. + ENDIF. + ENDIF. + +* the price of the glass + IF iv_glass = 'N'. + MOVE 0 TO lv_glass. + ELSE. + IF iv_glass = 'S'. + MOVE 18 TO lv_glass. + ELSE. + MOVE 34 TO lv_glass. + ENDIF. + ENDIF. + +* the extras + MOVE 3 TO lv_base. + MOVE 12 TO lv_labour. + MOVE 4 TO lv_small. + MOVE 25 TO lv_big. + MOVE 50 TO lv_limit_s. + MOVE 20000 TO lv_limit_b. + + COMPUTE lv_extra = lv_base + lv_labour. + + IF gv_perimeter LT lv_limit_s. + COMPUTE lv_extra = lv_extra + lv_small. + ENDIF. + + IF gv_area GT lv_limit_b. + COMPUTE lv_extra = lv_extra + lv_big. + ENDIF. + + COMPUTE lv_sum = lv_frame + lv_glass + lv_extra. + + MOVE iv_quantity TO lv_qty. + COMPUTE lv_sum = lv_sum * lv_qty. + + MOVE 15 TO lv_setup. + IF iv_quantity LT 5. + COMPUTE lv_sum = lv_sum + lv_setup. + ENDIF. + + rv_price = lv_sum. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/train/G1316/spec.md b/tasks_gen/train/G1316/spec.md new file mode 100644 index 0000000..04706c3 --- /dev/null +++ b/tasks_gen/train/G1316/spec.md @@ -0,0 +1 @@ +Hi, we need to refactor the class {{P}}FRAME_PRICER in package $TMP. The class is active. It is public, final, and has a public constructor without parameters. It has public types TY_MOULDING (CHAR 1), TY_GLASS (CHAR 1), and TY_AMOUNT (packed number with 2 decimal places). It has one public method CALCULATE_PRICE with importing parameters IV_WIDTH_CM, IV_HEIGHT_CM, IV_MOULDING, IV_GLASS, and IV_QUANTITY, and returning parameter RV_PRICE. The current implementation contains the complete calculation. Please read it. The code is hard to read and hard to maintain. Refactor the class. The behavior stays the same. Keep the class name, the class properties, and the complete public interface. Do not add, remove, or rename public methods, public types, or parameters. The method CALCULATE_PRICE must return the same value as before for every input. The behavior of the class stays the same. Every input combination that the current implementation accepts must produce the same result. The rules of the calculation are in the current implementation. Read them there. The hidden tests check the behavior with valid and invalid input values. The release target is 8.16. Follow Clean ABAP. Each method must have less than 40 statements. Use local variables instead of attributes. Use constants instead of magic numbers. Use modern ABAP statements and modern comparison operators. Give each method one clear task. Do not change the public interface. Do not create other objects. Do not change any other object in package $TMP. The class must be active and have no syntax error. The hidden tests must pass. The class must have no ATC priority 1 or priority 2 finding. Write your own ABAP Unit tests for the class. There are no open questions. diff --git a/tasks_gen/train/G1316/task.json b/tasks_gen/train/G1316/task.json new file mode 100644 index 0000000..d667753 --- /dev/null +++ b/tasks_gen/train/G1316/task.json @@ -0,0 +1,50 @@ +{ + "id": "G1316", + "category": "K", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 20 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER", + "file": "seed/frame_pricer.clas.abap", + "description": "Legacy class that calculates the price of a picture frame" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER_HIDDEN", + "file": "hidden/frame_pricer_hidden.clas.abap", + "description": "Hidden tests for the unchanged behavior of the frame price calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}FRAME_PRICER", + "file": "reference/frame_pricer.clas.abap", + "description": "Refactored frame price calculation", + "testclasses_file": "reference/frame_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "global_variables" + ], + "base_task": "G1014", + "input_style": "free_text" +} \ No newline at end of file diff --git a/tasks_gen/train/G1317/generation.json b/tasks_gen/train/G1317/generation.json new file mode 100644 index 0000000..3168e5a --- /dev/null +++ b/tasks_gen/train/G1317/generation.json @@ -0,0 +1,16 @@ +{ + "id": "G1317", + "pool": "train", + "category": "K", + "base_task": "G1015", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/train/G1317/hidden/t02_hidden.clas.abap b/tasks_gen/train/G1317/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..82be875 --- /dev/null +++ b/tasks_gen/train/G1317/hidden/t02_hidden.clas.abap @@ -0,0 +1,169 @@ +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}}towing_fee. + METHODS setup. + METHODS minimum_fee_applies FOR TESTING. + METHODS distance_charge FOR TESTING. + METHODS first_ten_km_free FOR TESTING. + METHODS night_surcharge FOR TESTING. + METHODS weekend_surcharge FOR TESTING. + METHODS night_and_weekend FOR TESTING. + METHODS night_hour_boundaries FOR TESTING. + METHODS rounds_up_to_five FOR TESTING. + METHODS large_vehicle_long_trip FOR TESTING. + METHODS rejects_unknown_class FOR TESTING. + METHODS rejects_bad_distance FOR TESTING. + METHODS rejects_bad_hour FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}towing_fee( ). + ENDMETHOD. + + METHOD minimum_fee_applies. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 5 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-ok + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 80 + act = ls_result-fee ). + ENDMETHOD. + + METHOD distance_charge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 130 + act = ls_result-fee ). + ENDMETHOD. + + METHOD first_ten_km_free. + DATA(ls_ten) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 12 + iv_is_weekend = abap_false ). + DATA(ls_twenty) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 20 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 90 + act = ls_ten-fee ). + cl_abap_unit_assert=>assert_equals( exp = 110 + act = ls_twenty-fee ). + ENDMETHOD. + + METHOD night_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 23 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 165 + act = ls_result-fee ). + ENDMETHOD. + + METHOD weekend_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 150 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_and_weekend. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 10 + iv_hour = 23 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 85 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_hour_boundaries. + DATA(ls_22) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 22 + iv_is_weekend = abap_false ). + DATA(ls_21) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 21 + iv_is_weekend = abap_false ). + DATA(ls_5) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 5 + iv_is_weekend = abap_false ). + DATA(ls_6) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 6 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 115 act = ls_22-fee msg = `hour 22` ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_21-fee msg = `hour 21` ). + cl_abap_unit_assert=>assert_equals( exp = 115 act = ls_5-fee msg = `hour 5` ). + cl_abap_unit_assert=>assert_equals( exp = 90 act = ls_6-fee msg = `hour 6` ). + ENDMETHOD. + + METHOD rounds_up_to_five. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 11 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 95 + act = ls_result-fee ). + ENDMETHOD. + + METHOD large_vehicle_long_trip. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'L' + iv_distance_km = 100 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 320 + act = ls_result-fee ). + ENDMETHOD. + + METHOD rejects_unknown_class. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'X' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_class + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = ls_result-fee ). + ENDMETHOD. + + METHOD rejects_bad_distance. + DATA(ls_zero) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 0 + iv_hour = 12 + iv_is_weekend = abap_false ). + DATA(ls_far) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 501 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_zero-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_far-status ). + ENDMETHOD. + + METHOD rejects_bad_hour. + DATA(ls_24) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = 24 + iv_is_weekend = abap_false ). + DATA(ls_neg) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 10 + iv_hour = -1 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_hour + act = ls_24-status ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_hour + act = ls_neg-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1317/reference/towing_fee.clas.abap b/tasks_gen/train/G1317/reference/towing_fee.clas.abap new file mode 100644 index 0000000..a64f45f --- /dev/null +++ b/tasks_gen/train/G1317/reference/towing_fee.clas.abap @@ -0,0 +1,133 @@ +CLASS {{p}}towing_fee DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 8. + TYPES ty_amount TYPE i. + TYPES ty_class TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + status TYPE ty_status, + fee TYPE ty_amount, + END OF ty_result. + + CONSTANTS: + BEGIN OF c_status, + ok TYPE ty_status VALUE 'OK', + bad_class TYPE ty_status VALUE 'CLASS', + bad_distance TYPE ty_status VALUE 'DISTANCE', + bad_hour TYPE ty_status VALUE 'HOUR', + END OF c_status. + + METHODS calculate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + iv_is_weekend TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_base_small TYPE ty_amount VALUE 60, + c_base_medium TYPE ty_amount VALUE 90, + c_base_large TYPE ty_amount VALUE 140, + c_free_km TYPE i VALUE 10, + c_km_rate TYPE ty_amount VALUE 2, + c_night_percent TYPE i VALUE 25, + c_weekend_percent TYPE i VALUE 15, + c_minimum_fee TYPE ty_amount VALUE 80, + c_round_step TYPE ty_amount VALUE 5, + c_max_distance TYPE i VALUE 500. + + METHODS validate + IMPORTING iv_vehicle_class TYPE ty_class + iv_distance_km TYPE i + iv_hour TYPE i + RETURNING VALUE(rv_status) TYPE ty_status. + METHODS base_fee + IMPORTING iv_vehicle_class TYPE ty_class + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS distance_fee + IMPORTING iv_distance_km TYPE i + RETURNING VALUE(rv_fee) TYPE ty_amount. + METHODS is_night + IMPORTING iv_hour TYPE i + RETURNING VALUE(rv_night) TYPE abap_bool. + METHODS surcharge + IMPORTING iv_subtotal TYPE ty_amount + iv_percent TYPE i + RETURNING VALUE(rv_surcharge) TYPE ty_amount. + METHODS round_up + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}towing_fee IMPLEMENTATION. + METHOD calculate. + DATA(lv_status) = validate( iv_vehicle_class = iv_vehicle_class + iv_distance_km = iv_distance_km + iv_hour = iv_hour ). + IF lv_status <> c_status-ok. + rs_result = VALUE #( status = lv_status fee = 0 ). + RETURN. + ENDIF. + + DATA(lv_subtotal) = base_fee( iv_vehicle_class ) + distance_fee( iv_distance_km ). + DATA(lv_total) = lv_subtotal. + IF is_night( iv_hour ) = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_night_percent ). + ENDIF. + IF iv_is_weekend = abap_true. + lv_total = lv_total + surcharge( iv_subtotal = lv_subtotal + iv_percent = c_weekend_percent ). + ENDIF. + IF lv_total < c_minimum_fee. + lv_total = c_minimum_fee. + ENDIF. + rs_result = VALUE #( status = c_status-ok + fee = round_up( lv_total ) ). + ENDMETHOD. + + METHOD validate. + IF iv_vehicle_class <> 'S' AND iv_vehicle_class <> 'M' AND iv_vehicle_class <> 'L'. + rv_status = c_status-bad_class. + ELSEIF iv_distance_km < 1 OR iv_distance_km > c_max_distance. + rv_status = c_status-bad_distance. + ELSEIF iv_hour < 0 OR iv_hour > 23. + rv_status = c_status-bad_hour. + ELSE. + rv_status = c_status-ok. + ENDIF. + ENDMETHOD. + + METHOD base_fee. + CASE iv_vehicle_class. + WHEN 'S'. + rv_fee = c_base_small. + WHEN 'M'. + rv_fee = c_base_medium. + WHEN 'L'. + rv_fee = c_base_large. + ENDCASE. + ENDMETHOD. + + METHOD distance_fee. + DATA(lv_chargeable_km) = iv_distance_km - c_free_km. + IF lv_chargeable_km < 0. + lv_chargeable_km = 0. + ENDIF. + rv_fee = lv_chargeable_km * c_km_rate. + ENDMETHOD. + + METHOD is_night. + rv_night = xsdbool( iv_hour >= 22 OR iv_hour <= 5 ). + ENDMETHOD. + + METHOD surcharge. + rv_surcharge = iv_subtotal * iv_percent DIV 100. + ENDMETHOD. + + METHOD round_up. + rv_amount = ( ( iv_amount + c_round_step - 1 ) DIV c_round_step ) * c_round_step. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1317/reference/towing_fee.testclasses.abap b/tasks_gen/train/G1317/reference/towing_fee.testclasses.abap new file mode 100644 index 0000000..f05c923 --- /dev/null +++ b/tasks_gen/train/G1317/reference/towing_fee.testclasses.abap @@ -0,0 +1,74 @@ +CLASS ltc_towing_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}towing_fee. + METHODS setup. + METHODS minimum_fee FOR TESTING. + METHODS distance_charge FOR TESTING. + METHODS night_surcharge FOR TESTING. + METHODS weekend_surcharge FOR TESTING. + METHODS invalid_class FOR TESTING. + METHODS invalid_distance FOR TESTING. +ENDCLASS. + + +CLASS ltc_towing_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}towing_fee( ). + ENDMETHOD. + + METHOD minimum_fee. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'S' + iv_distance_km = 3 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-ok + act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 80 + act = ls_result-fee ). + ENDMETHOD. + + METHOD distance_charge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 130 + act = ls_result-fee ). + ENDMETHOD. + + METHOD night_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 23 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = 165 + act = ls_result-fee ). + ENDMETHOD. + + METHOD weekend_surcharge. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = 150 + act = ls_result-fee ). + ENDMETHOD. + + METHOD invalid_class. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'X' + iv_distance_km = 30 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_class + act = ls_result-status ). + ENDMETHOD. + + METHOD invalid_distance. + DATA(ls_result) = mo_cut->calculate( iv_vehicle_class = 'M' + iv_distance_km = 0 + iv_hour = 12 + iv_is_weekend = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}towing_fee=>c_status-bad_distance + act = ls_result-status ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/train/G1317/spec.md b/tasks_gen/train/G1317/spec.md new file mode 100644 index 0000000..738baed --- /dev/null +++ b/tasks_gen/train/G1317/spec.md @@ -0,0 +1,18 @@ +Create the public final class {{P}}TOWING_FEE in package $TMP. The class has a public constructor without parameters. Define these public types: TY_STATUS (character field, length 8), TY_AMOUNT (integer), TY_CLASS (character field, length 1), and TY_RESULT (structure with components STATUS type TY_STATUS and FEE type TY_AMOUNT). Define the public constant structure C_STATUS with components OK, BAD_CLASS, BAD_DISTANCE, and BAD_HOUR. The values are 'OK', 'CLASS', 'DISTANCE', and 'HOUR'. The class has exactly one public method: CALCULATE, importing iv_vehicle_class TYPE ty_class, iv_distance_km TYPE i, iv_hour TYPE i, iv_is_weekend TYPE abap_bool, returning VALUE(rs_result) TYPE ty_result. Do not add other public methods. + +The method CALCULATE applies these rules in this sequence: +1. Check the input. Return the status of the first check that fails. If a check fails, the fee is 0. + 1.1 The vehicle class must be 'S', 'M', or 'L'. Else return BAD_CLASS. + 1.2 The distance must be from 1 to 500 kilometres. Else return BAD_DISTANCE. + 1.3 The hour must be from 0 to 23. Else return BAD_HOUR. +2. The base fee comes from the vehicle class: S = 60 EUR, M = 90 EUR, L = 140 EUR. +3. The distance charge: the first 10 kilometres are free. Each kilometre above 10 costs 2 EUR. +4. Add the base fee and the distance charge. The result is the subtotal. +5. The night is hour 22, hour 23, and hours 0 to 5. If the call is at night, calculate 25 percent of the subtotal. Round the night surcharge down to a full euro. +6. If the call is on a weekend, calculate 15 percent of the subtotal. Round the weekend surcharge down to a full euro. The night surcharge and the weekend surcharge can apply together. Calculate both surcharges from the subtotal. +7. Add the subtotal and all surcharges. The result is the total. +8. If the total is below 80 EUR, the total is 80 EUR. +9. Round the total up to a full 5 EUR. If the total is already a full 5 EUR, the total does not change. +10. Return the status OK and the fee. + +Do not create other objects. Write ABAP Unit tests for the class. diff --git a/tasks_gen/train/G1317/task.json b/tasks_gen/train/G1317/task.json new file mode 100644 index 0000000..09a5270 --- /dev/null +++ b/tasks_gen/train/G1317/task.json @@ -0,0 +1,43 @@ +{ + "id": "G1317", + "category": "K", + "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}}TOWING_FEE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the towing fee calculator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOWING_FEE", + "file": "reference/towing_fee.clas.abap", + "description": "Towing call-out fee calculator", + "testclasses_file": "reference/towing_fee.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ], + "base_task": "G1015", + "input_style": "incomplete" +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1000.json b/tasks_gen/train/_logs/G1000.json new file mode 100644 index 0000000..3bf664e --- /dev/null +++ b/tasks_gen/train/_logs/G1000.json @@ -0,0 +1,21 @@ +{ + "id": "G1000", + "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": 67.2403 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1001.json b/tasks_gen/train/_logs/G1001.json new file mode 100644 index 0000000..979d5f8 --- /dev/null +++ b/tasks_gen/train/_logs/G1001.json @@ -0,0 +1,21 @@ +{ + "id": "G1001", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.2007 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1002.json b/tasks_gen/train/_logs/G1002.json new file mode 100644 index 0000000..9a45066 --- /dev/null +++ b/tasks_gen/train/_logs/G1002.json @@ -0,0 +1,26 @@ +{ + "id": "G1002", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.2732 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1003.json b/tasks_gen/train/_logs/G1003.json new file mode 100644 index 0000000..71ab8e8 --- /dev/null +++ b/tasks_gen/train/_logs/G1003.json @@ -0,0 +1,26 @@ +{ + "id": "G1003", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 70.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.3906 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1004.json b/tasks_gen/train/_logs/G1004.json new file mode 100644 index 0000000..dadd3cf --- /dev/null +++ b/tasks_gen/train/_logs/G1004.json @@ -0,0 +1,26 @@ +{ + "id": "G1004", + "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": 67.5383 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1005.json b/tasks_gen/train/_logs/G1005.json new file mode 100644 index 0000000..114cb26 --- /dev/null +++ b/tasks_gen/train/_logs/G1005.json @@ -0,0 +1,26 @@ +{ + "id": "G1005", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 96.4, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.4885 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1006.json b/tasks_gen/train/_logs/G1006.json new file mode 100644 index 0000000..ec3bea0 --- /dev/null +++ b/tasks_gen/train/_logs/G1006.json @@ -0,0 +1,62 @@ +{ + "id": "G1006", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 47: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 53: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 59: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 65: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 71: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 77: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 89: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/t14_hidden.clas.abap line 96: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "Too close to task G0150 (similarity spec 0.60, rules 0.63, names 0.25). Choose a different business topic and different object names.", + "Too close to task G0151 (similarity spec 0.70, rules 0.63, names 0.33). Choose a different business topic and different object names.", + "Too close to task G0173 (similarity spec 0.65, rules 0.62, names 0.33). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 9, + "extra": 3 + } + }, + { + "stage": "bundle", + "errors": [ + "invalid JSON: Invalid \\escape: line 1 column 1374 (char 1373)" + ], + "by_check": { + "json": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "hidden/t14_hidden.clas.abap line 82: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"15\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"", + "hidden/t14_hidden.clas.abap line 83: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"56\"" + ], + "by_check": { + "abaplint": 3 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.7922 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1007.json b/tasks_gen/train/_logs/G1007.json new file mode 100644 index 0000000..2096b3d --- /dev/null +++ b/tasks_gen/train/_logs/G1007.json @@ -0,0 +1,16 @@ +{ + "id": "G1007", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.5562 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1008.json b/tasks_gen/train/_logs/G1008.json new file mode 100644 index 0000000..f3ac6ec --- /dev/null +++ b/tasks_gen/train/_logs/G1008.json @@ -0,0 +1,35 @@ +{ + "id": "G1008", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/turb_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 73.0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 67.8317 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1009.json b/tasks_gen/train/_logs/G1009.json new file mode 100644 index 0000000..1cf6d89 --- /dev/null +++ b/tasks_gen/train/_logs/G1009.json @@ -0,0 +1,31 @@ +{ + "id": "G1009", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 63.4, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 68.4828 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1010.json b/tasks_gen/train/_logs/G1010.json new file mode 100644 index 0000000..1e1eb46 --- /dev/null +++ b/tasks_gen/train/_logs/G1010.json @@ -0,0 +1,30 @@ +{ + "id": "G1010", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/phone_repair_pricer.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 67.965 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1011.json b/tasks_gen/train/_logs/G1011.json new file mode 100644 index 0000000..1744416 --- /dev/null +++ b/tasks_gen/train/_logs/G1011.json @@ -0,0 +1,21 @@ +{ + "id": "G1011", + "pool": "train", + "object_type": "DDLS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 68.084 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1012.json b/tasks_gen/train/_logs/G1012.json new file mode 100644 index 0000000..9f8d07a --- /dev/null +++ b/tasks_gen/train/_logs/G1012.json @@ -0,0 +1,31 @@ +{ + "id": "G1012", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 3, + "ok": false + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 68.7355 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1013.json b/tasks_gen/train/_logs/G1013.json new file mode 100644 index 0000000..faa7477 --- /dev/null +++ b/tasks_gen/train/_logs/G1013.json @@ -0,0 +1,21 @@ +{ + "id": "G1013", + "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": 68.084 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1014.json b/tasks_gen/train/_logs/G1014.json new file mode 100644 index 0000000..fae0d6c --- /dev/null +++ b/tasks_gen/train/_logs/G1014.json @@ -0,0 +1,54 @@ +{ + "id": "G1014", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/frame_pricer_hidden.clas.abap line 27: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 28: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"20\"", + "hidden/frame_pricer_hidden.clas.abap line 37: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 38: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 47: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 48: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 58: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"84\"", + "hidden/frame_pricer_hidden.clas.abap line 67: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 68: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 78: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"40\"", + "hidden/frame_pricer_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 88: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 97: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 98: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 107: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 108: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/frame_pricer_hidden.clas.abap line 117: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/frame_pricer_hidden.clas.abap line 118: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"" + ], + "by_check": { + "abaplint": 20 + } + }, + { + "stage": "validate", + "oracle": 79.2, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 69.2863 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1015.json b/tasks_gen/train/_logs/G1015.json new file mode 100644 index 0000000..125170f --- /dev/null +++ b/tasks_gen/train/_logs/G1015.json @@ -0,0 +1,21 @@ +{ + "id": "G1015", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 68.8365 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1016.json b/tasks_gen/train/_logs/G1016.json new file mode 100644 index 0000000..ecf4e5e --- /dev/null +++ b/tasks_gen/train/_logs/G1016.json @@ -0,0 +1,37 @@ +{ + "id": "G1016", + "pool": "train", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/return_checker.testclasses.abap: name damaged_without_code_is_rejected has 32 characters (ABAP max 30)", + "seed/vehicle.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)", + "seed/vehicle.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)" + ], + "by_check": { + "static": 3 + } + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 68.8033 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1017.json b/tasks_gen/train/_logs/G1017.json new file mode 100644 index 0000000..23e30cd --- /dev/null +++ b/tasks_gen/train/_logs/G1017.json @@ -0,0 +1,21 @@ +{ + "id": "G1017", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 69.3767 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1018.json b/tasks_gen/train/_logs/G1018.json new file mode 100644 index 0000000..cea27eb --- /dev/null +++ b/tasks_gen/train/_logs/G1018.json @@ -0,0 +1,54 @@ +{ + "id": "G1018", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/ad_test.clas.abap line 40: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 40: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "reference/ad_test.clas.abap line 45: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 45: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"80\"", + "reference/ad_test.clas.abap line 50: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "reference/ad_test.clas.abap line 50: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 51: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 51: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "hidden/ad_hidden.clas.abap line 57: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 57: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"90\"", + "hidden/ad_hidden.clas.abap line 63: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 63: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 69: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 69: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/ad_hidden.clas.abap line 75: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 75: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"82\"", + "hidden/ad_hidden.clas.abap line 81: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 81: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"84\"", + "hidden/ad_hidden.clas.abap line 87: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/ad_hidden.clas.abap line 87: syntax error for release v740sp05 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"62\"" + ], + "by_check": { + "abaplint": 20 + } + }, + { + "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": 69.2863 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1019.json b/tasks_gen/train/_logs/G1019.json new file mode 100644 index 0000000..b6c9731 --- /dev/null +++ b/tasks_gen/train/_logs/G1019.json @@ -0,0 +1,25 @@ +{ + "id": "G1019", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/bowling_fee.clas.abap line 17: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 68.8738 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1020.json b/tasks_gen/train/_logs/G1020.json new file mode 100644 index 0000000..36cc2d6 --- /dev/null +++ b/tasks_gen/train/_logs/G1020.json @@ -0,0 +1,31 @@ +{ + "id": "G1020", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "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": 69.8556 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1021.json b/tasks_gen/train/_logs/G1021.json new file mode 100644 index 0000000..2c7d183 --- /dev/null +++ b/tasks_gen/train/_logs/G1021.json @@ -0,0 +1,31 @@ +{ + "id": "G1021", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "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": null, + "spent_total": 70.1655 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1022.json b/tasks_gen/train/_logs/G1022.json new file mode 100644 index 0000000..f5c291f --- /dev/null +++ b/tasks_gen/train/_logs/G1022.json @@ -0,0 +1,21 @@ +{ + "id": "G1022", + "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": 69.0865 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1023.json b/tasks_gen/train/_logs/G1023.json new file mode 100644 index 0000000..2a4f594 --- /dev/null +++ b/tasks_gen/train/_logs/G1023.json @@ -0,0 +1,26 @@ +{ + "id": "G1023", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 88.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 70.3941 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1024.json b/tasks_gen/train/_logs/G1024.json new file mode 100644 index 0000000..04f5e15 --- /dev/null +++ b/tasks_gen/train/_logs/G1024.json @@ -0,0 +1,26 @@ +{ + "id": "G1024", + "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": 71.1427 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1025.json b/tasks_gen/train/_logs/G1025.json new file mode 100644 index 0000000..9902ad9 --- /dev/null +++ b/tasks_gen/train/_logs/G1025.json @@ -0,0 +1,40 @@ +{ + "id": "G1025", + "pool": "train", + "object_type": "CLAS", + "category": "F", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G0150 (similarity spec 0.66, rules 0.67, names 0.20). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/crane_hire_calc.clas.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"DATA\"", + "reference/crane_hire_calc.clas.abap line 92: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"IF\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 69.2863 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1026.json b/tasks_gen/train/_logs/G1026.json new file mode 100644 index 0000000..263a954 --- /dev/null +++ b/tasks_gen/train/_logs/G1026.json @@ -0,0 +1,30 @@ +{ + "id": "G1026", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t88_test.clas.abap line 64: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 70.8329 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1027.json b/tasks_gen/train/_logs/G1027.json new file mode 100644 index 0000000..6e02bff --- /dev/null +++ b/tasks_gen/train/_logs/G1027.json @@ -0,0 +1,32 @@ +{ + "id": "G1027", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/scaffold_fee.testclasses.abap line 19: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 19: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"20\"", + "reference/scaffold_fee.testclasses.abap line 25: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 25: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"41\"", + "reference/scaffold_fee.testclasses.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 30: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"06\"", + "reference/scaffold_fee.testclasses.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/scaffold_fee.testclasses.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"94\"" + ], + "by_check": { + "abaplint": 8 + } + }, + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.3408 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1028.json b/tasks_gen/train/_logs/G1028.json new file mode 100644 index 0000000..c99c48f --- /dev/null +++ b/tasks_gen/train/_logs/G1028.json @@ -0,0 +1,52 @@ +{ + "id": "G1028", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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/t20_test.clas.abap line 31: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.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/t20_hidden.clas.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"SELECT\"", + "hidden/t20_hidden.clas.abap line 56: 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": 69.9825 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1029.json b/tasks_gen/train/_logs/G1029.json new file mode 100644 index 0000000..5f685a1 --- /dev/null +++ b/tasks_gen/train/_logs/G1029.json @@ -0,0 +1,31 @@ +{ + "id": "G1029", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.1541 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1030.json b/tasks_gen/train/_logs/G1030.json new file mode 100644 index 0000000..2647463 --- /dev/null +++ b/tasks_gen/train/_logs/G1030.json @@ -0,0 +1,38 @@ +{ + "id": "G1030", + "pool": "train", + "object_type": "CLAS", + "category": "E", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tailor_pricer.clas.abap line 52: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "Too close to task G1002 (similarity spec 0.69, rules 0.62, names 0.33). Choose a different business topic and different object names.", + "Too close to task G1010 (similarity spec 0.57, rules 0.64, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "abaplint": 1, + "extra": 2 + } + }, + { + "stage": "validate", + "oracle": 65.8, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.9152 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1031.json b/tasks_gen/train/_logs/G1031.json new file mode 100644 index 0000000..228835d --- /dev/null +++ b/tasks_gen/train/_logs/G1031.json @@ -0,0 +1,21 @@ +{ + "id": "G1031", + "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": 70.816 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1032.json b/tasks_gen/train/_logs/G1032.json new file mode 100644 index 0000000..35c45a7 --- /dev/null +++ b/tasks_gen/train/_logs/G1032.json @@ -0,0 +1,30 @@ +{ + "id": "G1032", + "pool": "train", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/lesson.tabl.asabap: field name minutes is a reserved word; choose another name" + ], + "by_check": { + "static": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.4141 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1033.json b/tasks_gen/train/_logs/G1033.json new file mode 100644 index 0000000..b3afa0b --- /dev/null +++ b/tasks_gen/train/_logs/G1033.json @@ -0,0 +1,21 @@ +{ + "id": "G1033", + "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": 72.2466 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1034.json b/tasks_gen/train/_logs/G1034.json new file mode 100644 index 0000000..7966e28 --- /dev/null +++ b/tasks_gen/train/_logs/G1034.json @@ -0,0 +1,21 @@ +{ + "id": "G1034", + "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": 71.0524 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1035.json b/tasks_gen/train/_logs/G1035.json new file mode 100644 index 0000000..dbe4af1 --- /dev/null +++ b/tasks_gen/train/_logs/G1035.json @@ -0,0 +1,21 @@ +{ + "id": "G1035", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.6686 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1036.json b/tasks_gen/train/_logs/G1036.json new file mode 100644 index 0000000..9509593 --- /dev/null +++ b/tasks_gen/train/_logs/G1036.json @@ -0,0 +1,26 @@ +{ + "id": "G1036", + "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": 72.7975 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1037.json b/tasks_gen/train/_logs/G1037.json new file mode 100644 index 0000000..2e8f3fc --- /dev/null +++ b/tasks_gen/train/_logs/G1037.json @@ -0,0 +1,21 @@ +{ + "id": "G1037", + "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": 71.3408 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1038.json b/tasks_gen/train/_logs/G1038.json new file mode 100644 index 0000000..4b09420 --- /dev/null +++ b/tasks_gen/train/_logs/G1038.json @@ -0,0 +1,26 @@ +{ + "id": "G1038", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "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": 72.4531 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1039.json b/tasks_gen/train/_logs/G1039.json new file mode 100644 index 0000000..4da5e12 --- /dev/null +++ b/tasks_gen/train/_logs/G1039.json @@ -0,0 +1,16 @@ +{ + "id": "G1039", + "pool": "train", + "object_type": "CLAS", + "category": "H", + "attempts": [ + { + "stage": "validate", + "oracle": 100, + "null": 30 + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 72.9203 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1040.json b/tasks_gen/train/_logs/G1040.json new file mode 100644 index 0000000..96e02ef --- /dev/null +++ b/tasks_gen/train/_logs/G1040.json @@ -0,0 +1,21 @@ +{ + "id": "G1040", + "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": 71.6224 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1041.json b/tasks_gen/train/_logs/G1041.json new file mode 100644 index 0000000..bb2f0ba --- /dev/null +++ b/tasks_gen/train/_logs/G1041.json @@ -0,0 +1,31 @@ +{ + "id": "G1041", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 91.7, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false, + "error_kind": null, + "spent_total": 72.8443 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1043.json b/tasks_gen/train/_logs/G1043.json new file mode 100644 index 0000000..21adee6 --- /dev/null +++ b/tasks_gen/train/_logs/G1043.json @@ -0,0 +1,21 @@ +{ + "id": "G1043", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 71.9152 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1046.json b/tasks_gen/train/_logs/G1046.json new file mode 100644 index 0000000..8d94413 --- /dev/null +++ b/tasks_gen/train/_logs/G1046.json @@ -0,0 +1,54 @@ +{ + "id": "G1046", + "pool": "train", + "object_type": "FUNC", + "category": "G", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/tree_test.clas.abap line 39: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 42: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 45: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 51: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 57: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/tree_test.clas.abap line 73: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 49: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 56: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 63: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 70: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 77: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 84: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 91: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 98: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 106: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "hidden/tree_hidden.clas.abap line 115: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "by_check": { + "abaplint": 16 + } + }, + { + "stage": "bundle", + "errors": [ + "reference/tree_test.clas.abap line 64: syntax error for release v702 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"63\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 72.2685 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1049.json b/tasks_gen/train/_logs/G1049.json new file mode 100644 index 0000000..6724274 --- /dev/null +++ b/tasks_gen/train/_logs/G1049.json @@ -0,0 +1,30 @@ +{ + "id": "G1049", + "pool": "train", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/quote_merger.testclasses.abap line 96: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_quotes_a\"" + ], + "by_check": { + "abaplint": 1 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 72.6573 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1052.json b/tasks_gen/train/_logs/G1052.json new file mode 100644 index 0000000..1f1eba0 --- /dev/null +++ b/tasks_gen/train/_logs/G1052.json @@ -0,0 +1,21 @@ +{ + "id": "G1052", + "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": 72.8858 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1055.json b/tasks_gen/train/_logs/G1055.json new file mode 100644 index 0000000..108bd23 --- /dev/null +++ b/tasks_gen/train/_logs/G1055.json @@ -0,0 +1,40 @@ +{ + "id": "G1055", + "pool": "train", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "Too close to task G1038 (similarity spec 0.53, rules 0.66, names 0.25). Choose a different business topic and different object names." + ], + "by_check": { + "extra": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 88.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true, + "error_kind": null, + "spent_total": 73.2528 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1300.json b/tasks_gen/train/_logs/G1300.json new file mode 100644 index 0000000..26eb231 --- /dev/null +++ b/tasks_gen/train/_logs/G1300.json @@ -0,0 +1,18 @@ +{ + "id": "G1300", + "pool": "train", + "category": "K", + "base_task": "G1000", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "DDLS", + "spent_total": 69.4907 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1301.json b/tasks_gen/train/_logs/G1301.json new file mode 100644 index 0000000..8059673 --- /dev/null +++ b/tasks_gen/train/_logs/G1301.json @@ -0,0 +1,18 @@ +{ + "id": "G1301", + "pool": "train", + "category": "K", + "base_task": "G1001", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 69.5621 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1302.json b/tasks_gen/train/_logs/G1302.json new file mode 100644 index 0000000..fad2adc --- /dev/null +++ b/tasks_gen/train/_logs/G1302.json @@ -0,0 +1,18 @@ +{ + "id": "G1302", + "pool": "train", + "category": "K", + "base_task": "G1006", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "FUNC", + "spent_total": 69.6131 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1303.json b/tasks_gen/train/_logs/G1303.json new file mode 100644 index 0000000..a064b64 --- /dev/null +++ b/tasks_gen/train/_logs/G1303.json @@ -0,0 +1,18 @@ +{ + "id": "G1303", + "pool": "train", + "category": "K", + "base_task": "G1002", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 69.7569 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1304.json b/tasks_gen/train/_logs/G1304.json new file mode 100644 index 0000000..b8656f3 --- /dev/null +++ b/tasks_gen/train/_logs/G1304.json @@ -0,0 +1,18 @@ +{ + "id": "G1304", + "pool": "train", + "category": "K", + "base_task": "G1011", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "DDLS", + "spent_total": 69.8556 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1305.json b/tasks_gen/train/_logs/G1305.json new file mode 100644 index 0000000..7fb606f --- /dev/null +++ b/tasks_gen/train/_logs/G1305.json @@ -0,0 +1,18 @@ +{ + "id": "G1305", + "pool": "train", + "category": "K", + "base_task": "G1018", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "FUNC", + "spent_total": 69.9724 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1306.json b/tasks_gen/train/_logs/G1306.json new file mode 100644 index 0000000..68eaead --- /dev/null +++ b/tasks_gen/train/_logs/G1306.json @@ -0,0 +1,18 @@ +{ + "id": "G1306", + "pool": "train", + "category": "K", + "base_task": "G1003", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.0473 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1307.json b/tasks_gen/train/_logs/G1307.json new file mode 100644 index 0000000..c9a6ba1 --- /dev/null +++ b/tasks_gen/train/_logs/G1307.json @@ -0,0 +1,18 @@ +{ + "id": "G1307", + "pool": "train", + "category": "K", + "base_task": "G1012", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "DDLS", + "spent_total": 70.1655 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1308.json b/tasks_gen/train/_logs/G1308.json new file mode 100644 index 0000000..01622ad --- /dev/null +++ b/tasks_gen/train/_logs/G1308.json @@ -0,0 +1,18 @@ +{ + "id": "G1308", + "pool": "train", + "category": "K", + "base_task": "G1004", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.3941 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1309.json b/tasks_gen/train/_logs/G1309.json new file mode 100644 index 0000000..718c6d0 --- /dev/null +++ b/tasks_gen/train/_logs/G1309.json @@ -0,0 +1,18 @@ +{ + "id": "G1309", + "pool": "train", + "category": "K", + "base_task": "G1020", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "DDLS", + "spent_total": 70.4213 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1310.json b/tasks_gen/train/_logs/G1310.json new file mode 100644 index 0000000..01a7828 --- /dev/null +++ b/tasks_gen/train/_logs/G1310.json @@ -0,0 +1,18 @@ +{ + "id": "G1310", + "pool": "train", + "category": "K", + "base_task": "G1005", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.5374 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1311.json b/tasks_gen/train/_logs/G1311.json new file mode 100644 index 0000000..40cbe71 --- /dev/null +++ b/tasks_gen/train/_logs/G1311.json @@ -0,0 +1,18 @@ +{ + "id": "G1311", + "pool": "train", + "category": "K", + "base_task": "G1009", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.7024 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1312.json b/tasks_gen/train/_logs/G1312.json new file mode 100644 index 0000000..aa5b003 --- /dev/null +++ b/tasks_gen/train/_logs/G1312.json @@ -0,0 +1,18 @@ +{ + "id": "G1312", + "pool": "train", + "category": "K", + "base_task": "G1010", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.816 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1313.json b/tasks_gen/train/_logs/G1313.json new file mode 100644 index 0000000..80eb55d --- /dev/null +++ b/tasks_gen/train/_logs/G1313.json @@ -0,0 +1,18 @@ +{ + "id": "G1313", + "pool": "train", + "category": "K", + "base_task": "G1031", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "DDLS", + "spent_total": 70.8626 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1314.json b/tasks_gen/train/_logs/G1314.json new file mode 100644 index 0000000..13ca848 --- /dev/null +++ b/tasks_gen/train/_logs/G1314.json @@ -0,0 +1,18 @@ +{ + "id": "G1314", + "pool": "train", + "category": "K", + "base_task": "G1026", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "FUNC", + "spent_total": 70.8938 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1315.json b/tasks_gen/train/_logs/G1315.json new file mode 100644 index 0000000..8e50fa2 --- /dev/null +++ b/tasks_gen/train/_logs/G1315.json @@ -0,0 +1,18 @@ +{ + "id": "G1315", + "pool": "train", + "category": "K", + "base_task": "G1013", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 70.9593 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1316.json b/tasks_gen/train/_logs/G1316.json new file mode 100644 index 0000000..9d6d6fd --- /dev/null +++ b/tasks_gen/train/_logs/G1316.json @@ -0,0 +1,18 @@ +{ + "id": "G1316", + "pool": "train", + "category": "K", + "base_task": "G1014", + "style": "free_text", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 71.0019 +} \ No newline at end of file diff --git a/tasks_gen/train/_logs/G1317.json b/tasks_gen/train/_logs/G1317.json new file mode 100644 index 0000000..cb6af73 --- /dev/null +++ b/tasks_gen/train/_logs/G1317.json @@ -0,0 +1,18 @@ +{ + "id": "G1317", + "pool": "train", + "category": "K", + "base_task": "G1015", + "style": "incomplete", + "tool_schema": null, + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0 + } + ], + "accepted": true, + "object_type": "CLAS", + "spent_total": 71.0524 +} \ No newline at end of file diff --git a/tasks_gen/train/plan.json b/tasks_gen/train/plan.json new file mode 100644 index 0000000..fcec6f9 --- /dev/null +++ b/tasks_gen/train/plan.json @@ -0,0 +1,1599 @@ +{ + "slots": [ + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1000", + "run_base": 32000 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1001", + "run_base": 32040 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1002", + "run_base": 32080 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1003", + "run_base": 32120 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1004", + "run_base": 32160 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1005", + "run_base": 32200 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v702", + "id": "G1006", + "run_base": 32240 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rule for a boundary value", + "id": "G1007", + "run_base": 32280 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1008", + "run_base": 32320 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1009", + "run_base": 32360 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1010", + "run_base": 32400 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong join condition", + "id": "G1011", + "run_base": 32440 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1012", + "run_base": 32480 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1013", + "run_base": 32520 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1014", + "run_base": 32560 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1015", + "run_base": 32600 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1016", + "run_base": 32640 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1017", + "run_base": 32680 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v740sp05", + "id": "G1018", + "run_base": 32720 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "two rules contradict for one case", + "id": "G1019", + "run_base": 32760 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1020", + "run_base": 32800 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1021", + "run_base": 32840 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1022", + "run_base": 32880 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1023", + "run_base": 32920 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1024", + "run_base": 32960 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1025", + "run_base": 33000 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v702", + "id": "G1026", + "run_base": 33040 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rounding rule", + "id": "G1027", + "run_base": 33080 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1028", + "run_base": 33120 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1029", + "run_base": 33160 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1030", + "run_base": 33200 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong aggregation level", + "id": "G1031", + "run_base": 33240 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1032", + "run_base": 33280 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1033", + "run_base": 33320 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1034", + "run_base": 33360 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1035", + "run_base": 33400 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1036", + "run_base": 33440 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1037", + "run_base": 33480 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v740sp05", + "id": "G1038", + "run_base": 33520 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rule for an empty input", + "id": "G1039", + "run_base": 33560 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1040", + "run_base": 33600 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1041", + "run_base": 33640 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1042", + "run_base": 33680 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1043", + "run_base": 33720 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1044", + "run_base": 33760 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1045", + "run_base": 33800 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v702", + "id": "G1046", + "run_base": 33840 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rule for a boundary value", + "id": "G1047", + "run_base": 33880 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1048", + "run_base": 33920 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1049", + "run_base": 33960 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1050", + "run_base": 34000 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong join condition", + "id": "G1051", + "run_base": 34040 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1052", + "run_base": 34080 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1053", + "run_base": 34120 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1054", + "run_base": 34160 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1055", + "run_base": 34200 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1056", + "run_base": 34240 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1057", + "run_base": 34280 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v740sp05", + "id": "G1058", + "run_base": 34320 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "two rules contradict for one case", + "id": "G1059", + "run_base": 34360 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1060", + "run_base": 34400 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1061", + "run_base": 34440 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1062", + "run_base": 34480 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1063", + "run_base": 34520 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1064", + "run_base": 34560 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1065", + "run_base": 34600 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v702", + "id": "G1066", + "run_base": 34640 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rounding rule", + "id": "G1067", + "run_base": 34680 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1068", + "run_base": 34720 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1069", + "run_base": 34760 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1070", + "run_base": 34800 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong aggregation level", + "id": "G1071", + "run_base": 34840 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1072", + "run_base": 34880 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1073", + "run_base": 34920 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1074", + "run_base": 34960 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": null, + "id": "G1075", + "run_base": 35000 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1076", + "run_base": 35040 + }, + { + "category": "F", + "object_type": "CLAS", + "topic": null, + "id": "G1077", + "run_base": 35080 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v740sp05", + "id": "G1078", + "run_base": 35120 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rule for an empty input", + "id": "G1079", + "run_base": 35160 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1080", + "run_base": 35200 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1081", + "run_base": 35240 + }, + { + "category": "E", + "object_type": "CLAS", + "topic": null, + "id": "G1082", + "run_base": 35280 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": null, + "id": "G1083", + "run_base": 35320 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": null, + "id": "G1084", + "run_base": 35360 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1085", + "run_base": 35400 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v702", + "id": "G1086", + "run_base": 35440 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "missing rule for a boundary value", + "id": "G1087", + "run_base": 35480 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1088", + "run_base": 35520 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": null, + "id": "G1089", + "run_base": 35560 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1090", + "run_base": 35600 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong join condition", + "id": "G1091", + "run_base": 35640 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1092", + "run_base": 35680 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1093", + "run_base": 35720 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1094", + "run_base": 35760 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": null, + "id": "G1095", + "run_base": 35800 + }, + { + "category": "D", + "object_type": "FUNC", + "topic": null, + "id": "G1096", + "run_base": 35840 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1097", + "run_base": 35880 + }, + { + "category": "G", + "object_type": "FUNC", + "topic": "release target v740sp05", + "id": "G1098", + "run_base": 35920 + }, + { + "category": "H", + "object_type": "CLAS", + "topic": "two rules contradict for one case", + "id": "G1099", + "run_base": 35960 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1100", + "run_base": 36000 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1101", + "run_base": 36040 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1102", + "run_base": 36080 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": null, + "id": "G1103", + "run_base": 36120 + }, + { + "category": "D", + "object_type": "FUNC", + "topic": null, + "id": "G1104", + "run_base": 36160 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1105", + "run_base": 36200 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v702", + "id": "G1106", + "run_base": 36240 + }, + { + "category": "H", + "object_type": "FUNC", + "topic": "missing rule for an unknown code", + "id": "G1107", + "run_base": 36280 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1108", + "run_base": 36320 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1109", + "run_base": 36360 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1110", + "run_base": 36400 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong aggregation level", + "id": "G1111", + "run_base": 36440 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "aggregation with HAVING", + "id": "G1112", + "run_base": 36480 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1113", + "run_base": 36520 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1114", + "run_base": 36560 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": null, + "id": "G1115", + "run_base": 36600 + }, + { + "category": "D", + "object_type": "FUNC", + "topic": null, + "id": "G1116", + "run_base": 36640 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1117", + "run_base": 36680 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v740sp05", + "id": "G1118", + "run_base": 36720 + }, + { + "category": "H", + "object_type": "FUNC", + "topic": "contradicting priority of two rules", + "id": "G1119", + "run_base": 36760 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "CASE, cast and built-in functions", + "id": "G1120", + "run_base": 36800 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1121", + "run_base": 36840 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1122", + "run_base": 36880 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1123", + "run_base": 36920 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1124", + "run_base": 36960 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1125", + "run_base": 37000 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v702", + "id": "G1126", + "run_base": 37040 + }, + { + "category": "H", + "object_type": "FUNC", + "topic": "missing rule for an unknown code", + "id": "G1127", + "run_base": 37080 + }, + { + "category": "B", + "object_type": "DDLS", + "topic": "association used from ABAP SQL with a path expression", + "id": "G1128", + "run_base": 37120 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": null, + "id": "G1129", + "run_base": 37160 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1130", + "run_base": 37200 + }, + { + "category": "I", + "object_type": "DDLS", + "topic": "wrong join condition", + "id": "G1131", + "run_base": 37240 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1132", + "run_base": 37280 + }, + { + "category": "C", + "object_type": "PROG", + "topic": null, + "id": "G1133", + "run_base": 37320 + }, + { + "category": "E", + "object_type": "PROG", + "topic": null, + "id": "G1134", + "run_base": 37360 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1135", + "run_base": 37400 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1136", + "run_base": 37440 + }, + { + "category": "F", + "object_type": "FUNC", + "topic": null, + "id": "G1137", + "run_base": 37480 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v740sp05", + "id": "G1138", + "run_base": 37520 + }, + { + "category": "H", + "object_type": "FUNC", + "topic": "contradicting priority of two rules", + "id": "G1139", + "run_base": 37560 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1140", + "run_base": 37600 + }, + { + "category": "C", + "object_type": "PROG", + "topic": null, + "id": "G1141", + "run_base": 37640 + }, + { + "category": "E", + "object_type": "FUNC", + "topic": null, + "id": "G1142", + "run_base": 37680 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1143", + "run_base": 37720 + }, + { + "category": "D", + "object_type": "FUNC", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1144", + "run_base": 37760 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view on a seed table with unknown field names", + "id": "G1145", + "run_base": 37800 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v702", + "id": "G1146", + "run_base": 37840 + }, + { + "category": "H", + "object_type": "PROG", + "topic": "missing sort order or grouping rule", + "id": "G1147", + "run_base": 37880 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1148", + "run_base": 37920 + }, + { + "category": "C", + "object_type": "PROG", + "topic": null, + "id": "G1149", + "run_base": 37960 + }, + { + "category": "E", + "object_type": "FUNC", + "topic": null, + "id": "G1150", + "run_base": 38000 + }, + { + "category": "I", + "object_type": "CLAS", + "topic": null, + "id": "G1151", + "run_base": 38040 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1152", + "run_base": 38080 + }, + { + "category": "C", + "object_type": "PROG", + "topic": null, + "id": "G1153", + "run_base": 38120 + }, + { + "category": "E", + "object_type": "FUNC", + "topic": null, + "id": "G1154", + "run_base": 38160 + }, + { + "category": "A", + "object_type": "DDLS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1155", + "run_base": 38200 + }, + { + "category": "D", + "object_type": "DDLS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1156", + "run_base": 38240 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view that reuses a seed view", + "id": "G1157", + "run_base": 38280 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v740sp05", + "id": "G1158", + "run_base": 38320 + }, + { + "category": "H", + "object_type": "PROG", + "topic": "contradicting filter rules", + "id": "G1159", + "run_base": 38360 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1160", + "run_base": 38400 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1161", + "run_base": 38440 + }, + { + "category": "E", + "object_type": "FUNC", + "topic": null, + "id": "G1162", + "run_base": 38480 + }, + { + "category": "A", + "object_type": "PROG", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1163", + "run_base": 38520 + }, + { + "category": "D", + "object_type": "PROG", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1164", + "run_base": 38560 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view that uses a seed table function result table", + "id": "G1165", + "run_base": 38600 + }, + { + "category": "G", + "object_type": "PROG", + "topic": "release target v702", + "id": "G1166", + "run_base": 38640 + }, + { + "category": "H", + "object_type": "PROG", + "topic": "missing sort order or grouping rule", + "id": "G1167", + "run_base": 38680 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1168", + "run_base": 38720 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1169", + "run_base": 38760 + }, + { + "category": "E", + "object_type": "FUNC", + "topic": null, + "id": "G1170", + "run_base": 38800 + }, + { + "category": "I", + "object_type": "CLAS", + "topic": null, + "id": "G1171", + "run_base": 38840 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1172", + "run_base": 38880 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1173", + "run_base": 38920 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "old view with nested CASE to clean view entity", + "id": "G1174", + "run_base": 38960 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1175", + "run_base": 39000 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1176", + "run_base": 39040 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view on a seed table with unknown field names", + "id": "G1177", + "run_base": 39080 + }, + { + "category": "G", + "object_type": "CLAS", + "topic": "release target v740sp05", + "id": "G1178", + "run_base": 39120 + }, + { + "category": "H", + "object_type": "PROG", + "topic": "contradicting filter rules", + "id": "G1179", + "run_base": 39160 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1180", + "run_base": 39200 + }, + { + "category": "C", + "object_type": "DDLS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1181", + "run_base": 39240 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "duplicate join logic to one association", + "id": "G1182", + "run_base": 39280 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1183", + "run_base": 39320 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1184", + "run_base": 39360 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view that reuses a seed view", + "id": "G1185", + "run_base": 39400 + }, + { + "category": "G", + "object_type": "CLAS", + "topic": "release target v702", + "id": "G1186", + "run_base": 39440 + }, + { + "category": "H", + "object_type": "DDLS", + "topic": "missing rule for which records count", + "id": "G1187", + "run_base": 39480 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1188", + "run_base": 39520 + }, + { + "category": "C", + "object_type": "PROG", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1189", + "run_base": 39560 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "literal values to a parameter", + "id": "G1190", + "run_base": 39600 + }, + { + "category": "I", + "object_type": "CLAS", + "topic": null, + "id": "G1191", + "run_base": 39640 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1192", + "run_base": 39680 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1193", + "run_base": 39720 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "old view with nested CASE to clean view entity", + "id": "G1194", + "run_base": 39760 + }, + { + "category": "A", + "object_type": "CLAS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1195", + "run_base": 39800 + }, + { + "category": "D", + "object_type": "CLAS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1196", + "run_base": 39840 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view that uses a seed table function result table", + "id": "G1197", + "run_base": 39880 + }, + { + "category": "G", + "object_type": "CLAS", + "topic": "release target v740sp05", + "id": "G1198", + "run_base": 39920 + }, + { + "category": "H", + "object_type": "DDLS", + "topic": "contradicting join or filter rule", + "id": "G1199", + "run_base": 39960 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1200", + "run_base": 40000 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1201", + "run_base": 40040 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "duplicate join logic to one association", + "id": "G1202", + "run_base": 40080 + }, + { + "category": "A", + "object_type": "FUNC", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1203", + "run_base": 40120 + }, + { + "category": "D", + "object_type": "FUNC", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1204", + "run_base": 40160 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view on a seed table with unknown field names", + "id": "G1205", + "run_base": 40200 + }, + { + "category": "G", + "object_type": "CLAS", + "topic": "release target v702", + "id": "G1206", + "run_base": 40240 + }, + { + "category": "H", + "object_type": "DDLS", + "topic": "missing rule for which records count", + "id": "G1207", + "run_base": 40280 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1208", + "run_base": 40320 + }, + { + "category": "C", + "object_type": "CLAS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1209", + "run_base": 40360 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "literal values to a parameter", + "id": "G1210", + "run_base": 40400 + }, + { + "category": "I", + "object_type": "CLAS", + "topic": null, + "id": "G1211", + "run_base": 40440 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1212", + "run_base": 40480 + }, + { + "category": "C", + "object_type": "FUNC", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1213", + "run_base": 40520 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "old view with nested CASE to clean view entity", + "id": "G1214", + "run_base": 40560 + }, + { + "category": "A", + "object_type": "DDLS", + "topic": "The contract has methods with short coded character parameters (zone code, currency code, status flag, region key) of fixed length. A correct solution declares named types for them: TYPE c LENGTH n is not allowed in a method signature.", + "error_kind": "named-type", + "id": "G1215", + "run_base": 40600 + }, + { + "category": "D", + "object_type": "DDLS", + "topic": "The business domain uses concepts that collide with reserved or SQL words (hours, mode, order, date, count, group, value). The contract uses safe field and parameter names (for example work_hours, calc_mode); the solution must also avoid reserved words as field names.", + "error_kind": "reserved-word", + "id": "G1216", + "run_base": 40640 + }, + { + "category": "F", + "object_type": "DDLS", + "topic": "view that reuses a seed view", + "id": "G1217", + "run_base": 40680 + }, + { + "category": "G", + "object_type": "CLAS", + "topic": "release target v740sp05", + "id": "G1218", + "run_base": 40720 + }, + { + "category": "H", + "object_type": "DDLS", + "topic": "contradicting join or filter rule", + "id": "G1219", + "run_base": 40760 + }, + { + "category": "B", + "object_type": "PROG", + "topic": null, + "id": "G1220", + "run_base": 40800 + }, + { + "category": "C", + "object_type": "DDLS", + "topic": "The task has at least 10 separate business rules, so a good solution needs at least 10 own unit tests, each for one rule. Every ABAP name stays at 30 characters or less (the model must shorten long descriptive test method names).", + "error_kind": "long-names", + "id": "G1221", + "run_base": 40840 + }, + { + "category": "E", + "object_type": "DDLS", + "topic": "duplicate join logic to one association", + "id": "G1222", + "run_base": 40880 + } + ], + "ledger_at_start": 66.9665, + "stop_ledger": 27.0, + "target": 200, + "created": "2026-10-05 11:53:12" +} \ No newline at end of file