From 03ab36149ed116dba4ae3862d13ea1105096ae3c Mon Sep 17 00:00:00 2001 From: Kral Date: Sat, 3 Oct 2026 08:44:35 +0200 Subject: [PATCH] Fix coverage with several contract classes; table annotation and file path checks; clean task dir on write Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat --- harness/generator.py | 18 ++ harness/runner.py | 7 +- .../eval/G0001/seed/if_tour_planner.intf.abap | 55 ---- tasks_gen/eval/G0003/seed/course.ddls.abap | 8 - tasks_gen/eval/G0003/seed/course.ddls.asddls | 9 - tasks_gen/eval/G0005/seed/seed_data.clas.abap | 18 -- tasks_gen/eval/G0005/seed/zones.tabl.abap | 10 - .../seed/freight_calculator.testclasses.abap | 25 -- .../G0009/seed/shipping_cost_calc.clas.abap | 53 --- tasks_gen/eval/G0013/seed/repl_qty.func.abap | 38 --- tasks_gen/eval/G0016/seed/stock_net.prog.abap | 82 ----- .../G0018/reference/i_customer.ddls.asddls | 9 - .../G0026/reference/cx_rent_invalid.clas.abap | 30 -- .../G0100/faulty/m0_supplier_score.clas.abap | 88 +++++ .../G0100/faulty/m1_supplier_score.clas.abap | 88 +++++ .../G0100/faulty/m2_supplier_score.clas.abap | 88 +++++ .../G0100/faulty/m3_supplier_score.clas.abap | 88 +++++ .../G0100/faulty/m4_supplier_score.clas.abap | 88 +++++ tasks_gen/eval/G0100/generation.json | 29 ++ .../eval/G0100/hidden/t02_hidden.clas.abap | 98 ++++++ tasks_gen/eval/G0100/mutation.json | 79 +++++ .../G0100/reference/supplier_score.clas.abap | 88 +++++ .../reference/supplier_score.testclasses.abap | 54 ++++ tasks_gen/eval/G0100/spec.md | 62 ++++ tasks_gen/eval/G0100/task.json | 41 +++ .../faulty/m0_park_fee_calculator.clas.abap | 102 ++++++ .../faulty/m1_park_fee_calculator.clas.abap | 102 ++++++ .../faulty/m2_park_fee_calculator.clas.abap | 102 ++++++ .../faulty/m4_park_fee_calculator.clas.abap | 102 ++++++ tasks_gen/eval/G0101/generation.json | 29 ++ .../eval/G0101/hidden/t02_hidden.clas.abap | 156 +++++++++ tasks_gen/eval/G0101/mutation.json | 77 +++++ .../reference/park_fee_calculator.clas.abap | 102 ++++++ .../park_fee_calculator.testclasses.abap | 143 +++++++++ .../eval/G0101/seed/if_park_fee.intf.abap | 23 ++ tasks_gen/eval/G0101/spec.md | 67 ++++ tasks_gen/eval/G0101/task.json | 51 +++ .../eval/G0102/faulty/m0_hotel_rate.clas.abap | 108 +++++++ .../eval/G0102/faulty/m1_hotel_rate.clas.abap | 108 +++++++ .../eval/G0102/faulty/m2_hotel_rate.clas.abap | 108 +++++++ .../eval/G0102/faulty/m3_hotel_rate.clas.abap | 108 +++++++ .../eval/G0102/faulty/m4_hotel_rate.clas.abap | 108 +++++++ tasks_gen/eval/G0102/generation.json | 19 ++ .../eval/G0102/hidden/t13_hidden.clas.abap | 145 +++++++++ tasks_gen/eval/G0102/mutation.json | 74 +++++ .../eval/G0102/reference/hotel_rate.clas.abap | 108 +++++++ .../reference/hotel_rate.testclasses.abap | 76 +++++ .../eval/G0102/seed/if_hotel_rate.intf.abap | 29 ++ tasks_gen/eval/G0102/spec.md | 71 ++++ tasks_gen/eval/G0102/task.json | 51 +++ .../G0103/faulty/m0_overdue_fine.clas.abap | 68 ++++ .../G0103/faulty/m1_overdue_fine.clas.abap | 68 ++++ .../G0103/faulty/m2_overdue_fine.clas.abap | 68 ++++ .../G0103/faulty/m3_overdue_fine.clas.abap | 68 ++++ .../G0103/faulty/m4_overdue_fine.clas.abap | 68 ++++ tasks_gen/eval/G0103/generation.json | 19 ++ .../eval/G0103/hidden/t02_hidden.clas.abap | 103 ++++++ tasks_gen/eval/G0103/mutation.json | 77 +++++ .../G0103/reference/overdue_fine.clas.abap | 68 ++++ .../reference/overdue_fine.testclasses.abap | 89 ++++++ tasks_gen/eval/G0103/spec.md | 56 ++++ tasks_gen/eval/G0103/task.json | 41 +++ .../faulty/m0_insurance_premium.clas.abap | 96 ++++++ .../faulty/m1_insurance_premium.clas.abap | 96 ++++++ .../faulty/m2_insurance_premium.clas.abap | 96 ++++++ .../faulty/m3_insurance_premium.clas.abap | 96 ++++++ .../faulty/m4_insurance_premium.clas.abap | 96 ++++++ tasks_gen/eval/G0104/generation.json | 24 ++ .../eval/G0104/hidden/t02_hidden.clas.abap | 111 +++++++ tasks_gen/eval/G0104/mutation.json | 83 +++++ .../reference/insurance_premium.clas.abap | 96 ++++++ .../insurance_premium.testclasses.abap | 110 +++++++ tasks_gen/eval/G0104/spec.md | 63 ++++ tasks_gen/eval/G0104/task.json | 40 +++ .../eval/G0105/faulty/m0_toll_calc.clas.abap | 100 ++++++ .../eval/G0105/faulty/m1_toll_calc.clas.abap | 100 ++++++ .../eval/G0105/faulty/m2_toll_calc.clas.abap | 100 ++++++ .../eval/G0105/faulty/m3_toll_calc.clas.abap | 100 ++++++ .../eval/G0105/faulty/m4_toll_calc.clas.abap | 100 ++++++ tasks_gen/eval/G0105/generation.json | 33 ++ .../eval/G0105/hidden/t14_hidden.clas.abap | 140 ++++++++ tasks_gen/eval/G0105/mutation.json | 66 ++++ .../eval/G0105/reference/t14_test.clas.abap | 62 ++++ .../eval/G0105/reference/toll_calc.clas.abap | 100 ++++++ .../reference/toll_calc.testclasses.abap | 65 ++++ .../eval/G0105/reference/toll_fee.func.abap | 20 ++ tasks_gen/eval/G0105/spec.md | 86 +++++ tasks_gen/eval/G0105/task.json | 82 +++++ .../eval/G0106/faulty/m0_app_scorer.clas.abap | 102 ++++++ .../eval/G0106/faulty/m1_appl_score.func.abap | 31 ++ .../eval/G0106/faulty/m2_app_scorer.clas.abap | 102 ++++++ .../eval/G0106/faulty/m3_appl_score.func.abap | 31 ++ .../eval/G0106/faulty/m4_app_scorer.clas.abap | 102 ++++++ tasks_gen/eval/G0106/generation.json | 19 ++ .../eval/G0106/hidden/t41_hidden.clas.abap | 138 ++++++++ tasks_gen/eval/G0106/mutation.json | 74 +++++ .../eval/G0106/reference/app_scorer.clas.abap | 102 ++++++ .../app_scorer.clas.testclasses.abap | 33 ++ .../eval/G0106/reference/appl_score.func.abap | 31 ++ .../eval/G0106/reference/t41_test.clas.abap | 61 ++++ .../eval/G0106/seed/appl_sample.func.abap | 15 + tasks_gen/eval/G0106/spec.md | 105 ++++++ tasks_gen/eval/G0106/task.json | 95 ++++++ .../G0107/faulty/m0_energy_bill.func.abap | 51 +++ .../G0107/faulty/m1_energy_bill.func.abap | 51 +++ .../G0107/faulty/m2_energy_bill.func.abap | 51 +++ .../G0107/faulty/m3_energy_bill.func.abap | 51 +++ .../G0107/faulty/m4_energy_bill.func.abap | 51 +++ tasks_gen/eval/G0107/generation.json | 29 ++ .../eval/G0107/hidden/t21_hidden.clas.abap | 100 ++++++ tasks_gen/eval/G0107/mutation.json | 80 +++++ .../G0107/reference/energy_bill.func.abap | 51 +++ .../eval/G0107/reference/t21_test.clas.abap | 68 ++++ tasks_gen/eval/G0107/spec.md | 61 ++++ tasks_gen/eval/G0107/task.json | 71 ++++ .../G0108/faulty/m0_i_call_charge.ddls.asddls | 32 ++ .../G0108/faulty/m1_i_call_charge.ddls.asddls | 32 ++ .../G0108/faulty/m2_i_call_charge.ddls.asddls | 32 ++ .../G0108/faulty/m4_i_call_charge.ddls.asddls | 32 ++ tasks_gen/eval/G0108/generation.json | 34 ++ .../eval/G0108/hidden/t16_hidden.clas.abap | 110 +++++++ tasks_gen/eval/G0108/mutation.json | 55 ++++ .../G0108/reference/i_call_charge.ddls.asddls | 32 ++ .../eval/G0108/reference/t16_test.clas.abap | 52 +++ tasks_gen/eval/G0108/seed/calls.tabl.asabap | 14 + tasks_gen/eval/G0108/spec.md | 37 +++ tasks_gen/eval/G0108/task.json | 59 ++++ .../G0109/faulty/m0_i_manager.ddls.asddls | 11 + .../G0109/faulty/m1_i_employee.ddls.asddls | 13 + .../G0109/faulty/m2_i_manager.ddls.asddls | 11 + tasks_gen/eval/G0109/generation.json | 29 ++ .../eval/G0109/hidden/t31_hidden.clas.abap | 92 ++++++ tasks_gen/eval/G0109/mutation.json | 44 +++ .../G0109/reference/i_employee.ddls.asddls | 13 + .../G0109/reference/i_manager.ddls.asddls | 11 + .../eval/G0109/reference/t31_test.clas.abap | 47 +++ tasks_gen/eval/G0109/seed/emp.tabl.asabap | 13 + tasks_gen/eval/G0109/seed/seed_data.clas.abap | 23 ++ tasks_gen/eval/G0109/spec.md | 51 +++ tasks_gen/eval/G0109/task.json | 80 +++++ .../G0110/faulty/m1_i_prod_sales.ddls.asddls | 16 + .../G0110/faulty/m2_i_prod_sales.ddls.asddls | 16 + .../G0110/faulty/m3_i_prod_sales.ddls.asddls | 16 + .../G0110/faulty/m4_i_prod_sales.ddls.asddls | 16 + tasks_gen/eval/G0110/generation.json | 19 ++ .../eval/G0110/hidden/t16_hidden.clas.abap | 74 +++++ tasks_gen/eval/G0110/mutation.json | 61 ++++ .../G0110/reference/i_prod_sales.ddls.asddls | 16 + .../eval/G0110/reference/t16_test.clas.abap | 38 +++ tasks_gen/eval/G0110/seed/prod.tabl.asabap | 10 + tasks_gen/eval/G0110/seed/sale.tabl.asabap | 12 + tasks_gen/eval/G0110/spec.md | 37 +++ tasks_gen/eval/G0110/task.json | 65 ++++ .../eval/G0111/faulty/m1_court_use.prog.abap | 136 ++++++++ .../eval/G0111/faulty/m2_court_use.prog.abap | 136 ++++++++ tasks_gen/eval/G0111/generation.json | 19 ++ .../eval/G0111/hidden/court_hidden.clas.abap | 118 +++++++ tasks_gen/eval/G0111/mutation.json | 39 +++ .../eval/G0111/reference/court_use.prog.abap | 136 ++++++++ tasks_gen/eval/G0111/seed/booking.tabl.asabap | 13 + tasks_gen/eval/G0111/seed/court.tabl.asabap | 11 + .../eval/G0111/seed/court_seed.clas.abap | 25 ++ tasks_gen/eval/G0111/spec.md | 46 +++ tasks_gen/eval/G0111/task.json | 68 ++++ .../eval/G0112/faulty/m1_waste_cost.prog.abap | 143 +++++++++ .../eval/G0112/faulty/m2_waste_cost.prog.abap | 143 +++++++++ .../eval/G0112/faulty/m3_waste_cost.prog.abap | 143 +++++++++ tasks_gen/eval/G0112/generation.json | 34 ++ .../eval/G0112/hidden/waste_hidden.clas.abap | 160 ++++++++++ tasks_gen/eval/G0112/mutation.json | 57 ++++ .../eval/G0112/reference/waste_cost.prog.abap | 143 +++++++++ tasks_gen/eval/G0112/seed/pickup.tabl.asabap | 13 + tasks_gen/eval/G0112/seed/price.tabl.asabap | 10 + .../eval/G0112/seed/waste_seed.clas.abap | 26 ++ tasks_gen/eval/G0112/spec.md | 55 ++++ tasks_gen/eval/G0112/task.json | 68 ++++ .../G0113/faulty/m0_sample_batcher.clas.abap | 137 ++++++++ .../G0113/faulty/m1_sample_batcher.clas.abap | 137 ++++++++ .../G0113/faulty/m2_sample_batcher.clas.abap | 137 ++++++++ .../G0113/faulty/m3_sample_batcher.clas.abap | 137 ++++++++ .../G0113/faulty/m4_sample_batcher.clas.abap | 137 ++++++++ tasks_gen/eval/G0113/generation.json | 29 ++ .../eval/G0113/hidden/t02_hidden.clas.abap | 143 +++++++++ tasks_gen/eval/G0113/mutation.json | 71 ++++ .../G0113/reference/sample_batcher.clas.abap | 137 ++++++++ .../reference/sample_batcher.testclasses.abap | 88 +++++ tasks_gen/eval/G0113/spec.md | 77 +++++ tasks_gen/eval/G0113/task.json | 42 +++ .../faulty/m0_dispatch_planner.clas.abap | 110 +++++++ .../faulty/m1_dispatch_planner.clas.abap | 110 +++++++ .../faulty/m2_dispatch_planner.clas.abap | 110 +++++++ .../faulty/m3_dispatch_planner.clas.abap | 110 +++++++ .../faulty/m4_dispatch_planner.clas.abap | 110 +++++++ tasks_gen/eval/G0114/generation.json | 24 ++ .../eval/G0114/hidden/t02_hidden.clas.abap | 238 ++++++++++++++ tasks_gen/eval/G0114/mutation.json | 62 ++++ .../reference/dispatch_planner.clas.abap | 110 +++++++ .../dispatch_planner.testclasses.abap | 126 ++++++++ tasks_gen/eval/G0114/spec.md | 68 ++++ tasks_gen/eval/G0114/task.json | 42 +++ .../G0115/faulty/m0_refill_planner.clas.abap | 164 ++++++++++ .../G0115/faulty/m1_refill_planner.clas.abap | 164 ++++++++++ .../G0115/faulty/m2_refill_planner.clas.abap | 164 ++++++++++ .../G0115/faulty/m3_refill_planner.clas.abap | 164 ++++++++++ .../G0115/faulty/m4_refill_planner.clas.abap | 164 ++++++++++ tasks_gen/eval/G0115/generation.json | 19 ++ .../eval/G0115/hidden/refill_hidden.clas.abap | 151 +++++++++ tasks_gen/eval/G0115/mutation.json | 54 ++++ .../G0115/reference/refill_planner.clas.abap | 164 ++++++++++ .../reference/refill_planner.testclasses.abap | 93 ++++++ tasks_gen/eval/G0115/spec.md | 98 ++++++ tasks_gen/eval/G0115/task.json | 42 +++ .../faulty/m0_departure_planner.clas.abap | 93 ++++++ .../faulty/m1_departure_planner.clas.abap | 93 ++++++ .../faulty/m2_departure_planner.clas.abap | 93 ++++++ .../faulty/m3_departure_planner.clas.abap | 93 ++++++ .../faulty/m4_departure_planner.clas.abap | 93 ++++++ tasks_gen/eval/G0116/generation.json | 19 ++ .../eval/G0116/hidden/t02_hidden.clas.abap | 133 ++++++++ tasks_gen/eval/G0116/mutation.json | 60 ++++ .../reference/departure_planner.clas.abap | 93 ++++++ .../departure_planner.testclasses.abap | 73 +++++ tasks_gen/eval/G0116/spec.md | 68 ++++ tasks_gen/eval/G0116/task.json | 42 +++ .../eval/G0117/faulty/m0_tool_crib.clas.abap | 107 +++++++ .../eval/G0117/faulty/m1_tool_crib.clas.abap | 107 +++++++ .../eval/G0117/faulty/m2_tool_crib.clas.abap | 107 +++++++ .../eval/G0117/faulty/m3_tool_crib.clas.abap | 107 +++++++ .../eval/G0117/faulty/m4_tool_crib.clas.abap | 107 +++++++ tasks_gen/eval/G0117/generation.json | 29 ++ .../eval/G0117/hidden/t11_hidden.clas.abap | 157 +++++++++ tasks_gen/eval/G0117/mutation.json | 68 ++++ .../eval/G0117/reference/tool_crib.clas.abap | 107 +++++++ .../reference/tool_crib.testclasses.abap | 90 ++++++ .../eval/G0117/seed/tool_master.clas.abap | 20 ++ tasks_gen/eval/G0117/spec.md | 93 ++++++ tasks_gen/eval/G0117/task.json | 51 +++ .../G0118/faulty/m1_canteen_planner.clas.abap | 102 ++++++ .../G0118/faulty/m2_canteen_planner.clas.abap | 102 ++++++ .../G0118/faulty/m3_canteen_planner.clas.abap | 102 ++++++ .../G0118/faulty/m4_canteen_planner.clas.abap | 102 ++++++ tasks_gen/eval/G0118/generation.json | 24 ++ .../G0118/hidden/canteen_hidden.clas.abap | 231 ++++++++++++++ tasks_gen/eval/G0118/mutation.json | 62 ++++ .../G0118/reference/canteen_planner.clas.abap | 102 ++++++ .../canteen_planner.testclasses.abap | 73 +++++ .../eval/G0118/seed/if_canteen_plan.intf.abap | 42 +++ tasks_gen/eval/G0118/spec.md | 87 +++++ tasks_gen/eval/G0118/task.json | 52 +++ .../faulty/m0_workload_planner.clas.abap | 84 +++++ .../faulty/m1_workload_planner.clas.abap | 84 +++++ .../faulty/m2_workload_planner.clas.abap | 84 +++++ .../faulty/m3_workload_planner.clas.abap | 84 +++++ .../faulty/m4_workload_planner.clas.abap | 84 +++++ tasks_gen/eval/G0119/generation.json | 19 ++ .../eval/G0119/hidden/t02_hidden.clas.abap | 175 ++++++++++ tasks_gen/eval/G0119/mutation.json | 69 ++++ .../reference/workload_planner.clas.abap | 84 +++++ .../workload_planner.testclasses.abap | 85 +++++ tasks_gen/eval/G0119/spec.md | 54 ++++ tasks_gen/eval/G0119/task.json | 42 +++ .../faulty/m0_meter_consumption.clas.abap | 74 +++++ .../faulty/m1_meter_consumption.clas.abap | 74 +++++ .../faulty/m2_meter_consumption.clas.abap | 74 +++++ .../faulty/m4_meter_consumption.clas.abap | 74 +++++ tasks_gen/eval/G0120/generation.json | 19 ++ .../eval/G0120/hidden/t02_hidden.clas.abap | 151 +++++++++ tasks_gen/eval/G0120/mutation.json | 70 ++++ .../reference/meter_consumption.clas.abap | 74 +++++ .../meter_consumption.testclasses.abap | 75 +++++ .../G0120/seed/if_meter_reading.intf.abap | 23 ++ tasks_gen/eval/G0120/spec.md | 57 ++++ tasks_gen/eval/G0120/task.json | 51 +++ tasks_gen/eval/G0121/generation.json | 29 ++ .../eval/G0121/hidden/t14_hidden.clas.abap | 220 +++++++++++++ .../eval/G0121/reference/roast_sum.func.abap | 45 +++ .../eval/G0121/reference/t14_test.clas.abap | 109 +++++++ tasks_gen/eval/G0121/seed/blends.tabl.abap | 7 + .../eval/G0121/seed/roast_seed.clas.abap | 28 ++ tasks_gen/eval/G0121/seed/roasts.tabl.abap | 8 + tasks_gen/eval/G0121/spec.md | 52 +++ tasks_gen/eval/G0121/task.json | 92 ++++++ tasks_gen/eval/G0122/generation.json | 29 ++ .../eval/G0122/hidden/t31_hidden.clas.abap | 302 ++++++++++++++++++ .../G0122/reference/bike_rebalance.func.abap | 65 ++++ .../eval/G0122/reference/t31_test.clas.abap | 130 ++++++++ tasks_gen/eval/G0122/seed/move.tabl.abap | 12 + tasks_gen/eval/G0122/seed/seed_bike.clas.abap | 39 +++ tasks_gen/eval/G0122/seed/station.tabl.abap | 12 + tasks_gen/eval/G0122/spec.md | 85 +++++ tasks_gen/eval/G0122/task.json | 90 ++++++ .../eval/G0123/faulty/m0_lift_stats.func.abap | 91 ++++++ .../eval/G0123/faulty/m1_lift_stats.func.abap | 91 ++++++ .../eval/G0123/faulty/m2_lift_stats.func.abap | 91 ++++++ .../eval/G0123/faulty/m4_lift_stats.func.abap | 91 ++++++ tasks_gen/eval/G0123/generation.json | 29 ++ .../eval/G0123/hidden/t21_hidden.clas.abap | 125 ++++++++ tasks_gen/eval/G0123/mutation.json | 60 ++++ .../eval/G0123/reference/lift_stats.func.abap | 91 ++++++ .../eval/G0123/reference/t21_test.clas.abap | 53 +++ tasks_gen/eval/G0123/spec.md | 55 ++++ tasks_gen/eval/G0123/task.json | 76 +++++ tasks_gen/eval/G0124/generation.json | 38 +++ .../eval/G0124/hidden/t20_hidden.clas.abap | 212 ++++++++++++ tasks_gen/eval/G0124/mutation.json | 96 ++++++ .../eval/G0124/reference/milk_sum.prog.abap | 170 ++++++++++ tasks_gen/eval/G0124/seed/farm.tabl.asabap | 11 + tasks_gen/eval/G0124/seed/milk.tabl.asabap | 12 + tasks_gen/eval/G0124/seed/t20_seed.clas.abap | 26 ++ tasks_gen/eval/G0124/spec.md | 47 +++ tasks_gen/eval/G0124/task.json | 68 ++++ .../eval/G0125/faulty/m0_flight_sum.prog.abap | 186 +++++++++++ .../eval/G0125/faulty/m1_flight_sum.prog.abap | 186 +++++++++++ tasks_gen/eval/G0125/generation.json | 29 ++ .../eval/G0125/hidden/flight_hidden.clas.abap | 99 ++++++ tasks_gen/eval/G0125/mutation.json | 40 +++ .../eval/G0125/reference/flight_sum.prog.abap | 186 +++++++++++ tasks_gen/eval/G0125/seed/airport.tabl.asabap | 10 + tasks_gen/eval/G0125/seed/flight.tabl.asabap | 12 + .../eval/G0125/seed/flight_seed.clas.abap | 24 ++ tasks_gen/eval/G0125/spec.md | 43 +++ tasks_gen/eval/G0125/task.json | 67 ++++ tasks_gen/eval/G0126/generation.json | 34 ++ .../G0126/hidden/t_bag_drop_hidden.clas.abap | 219 +++++++++++++ .../eval/G0126/reference/bag_drop.clas.abap | 93 ++++++ .../G0126/reference/bag_drop.testclasses.abap | 218 +++++++++++++ .../G0126/reference/cx_bag_late.clas.abap | 13 + .../G0126/reference/cx_bag_limit.clas.abap | 13 + .../G0126/reference/cx_bag_size.clas.abap | 13 + .../eval/G0126/reference/cx_bag_tag.clas.abap | 13 + .../G0126/reference/cx_bag_weight.clas.abap | 13 + tasks_gen/eval/G0126/spec.md | 93 ++++++ tasks_gen/eval/G0126/task.json | 94 ++++++ .../faulty/m0_ferry_load_check.clas.abap | 92 ++++++ .../faulty/m1_ferry_load_check.clas.abap | 92 ++++++ .../faulty/m2_ferry_load_check.clas.abap | 92 ++++++ .../faulty/m3_ferry_load_check.clas.abap | 92 ++++++ .../faulty/m4_ferry_load_check.clas.abap | 92 ++++++ .../G0127/files/hidden/t02_hidden.clas.abap | 254 +++++++++++++++ .../files/reference/cx_ferry_error.clas.abap | 15 + .../reference/ferry_load_check.clas.abap | 92 ++++++ .../ferry_load_check.testclasses.abap | 114 +++++++ .../files/seed/if_ferry_reason.intf.abap | 14 + tasks_gen/eval/G0127/generation.json | 29 ++ tasks_gen/eval/G0127/mutation.json | 81 +++++ tasks_gen/eval/G0127/spec.md | 69 ++++ tasks_gen/eval/G0127/task.json | 60 ++++ tasks_gen/eval/G0128/generation.json | 29 ++ .../G0128/hidden/crew_check_hidden.clas.abap | 172 ++++++++++ tasks_gen/eval/G0128/mutation.json | 71 ++++ .../G0128/reference/crew_duty_check.clas.abap | 69 ++++ .../crew_duty_check.testclasses.abap | 151 +++++++++ .../reference/cx_duty_too_long.clas.abap | 23 ++ .../reference/cx_licence_invalid.clas.abap | 12 + .../G0128/reference/cx_month_limit.clas.abap | 23 ++ .../reference/cx_rating_missing.clas.abap | 26 ++ .../reference/cx_rest_too_short.clas.abap | 23 ++ tasks_gen/eval/G0128/spec.md | 100 ++++++ tasks_gen/eval/G0128/task.json | 97 ++++++ tasks_gen/eval/G0129/generation.json | 69 ++++ .../G0129/hidden/t_print_hidden.clas.abap | 267 ++++++++++++++++ .../reference/cx_print_job_error.clas.abap | 31 ++ .../G0129/reference/cx_print_limit.clas.abap | 20 ++ .../reference/print_job_pricer.clas.abap | 158 +++++++++ .../print_job_pricer.testclasses.abap | 104 ++++++ tasks_gen/eval/G0129/spec.md | 85 +++++ tasks_gen/eval/G0129/task.json | 66 ++++ tasks_gen/eval/G0130/generation.json | 33 ++ .../eval/G0130/hidden/t02_hidden.clas.abap | 156 +++++++++ .../G0130/reference/cx_presc_error.clas.abap | 60 ++++ .../reference/prescription_check.clas.abap | 103 ++++++ .../prescription_check.testclasses.abap | 104 ++++++ .../eval/G0130/seed/drug_master.clas.abap | 38 +++ tasks_gen/eval/G0130/spec.md | 84 +++++ tasks_gen/eval/G0130/task.json | 60 ++++ .../G0188/faulty/m0_laundry_pricer.clas.abap | 79 +++++ .../G0188/faulty/m1_laundry_pricer.clas.abap | 79 +++++ .../G0188/faulty/m2_laundry_pricer.clas.abap | 79 +++++ .../G0188/faulty/m3_laundry_pricer.clas.abap | 79 +++++ tasks_gen/eval/G0188/generation.json | 24 ++ .../eval/G0188/hidden/t02_hidden.clas.abap | 113 +++++++ tasks_gen/eval/G0188/mutation.json | 74 +++++ .../G0188/reference/laundry_pricer.clas.abap | 79 +++++ .../reference/laundry_pricer.testclasses.abap | 62 ++++ tasks_gen/eval/G0188/review.json | 7 + .../eval/G0188/seed/laundry_pricer.clas.abap | 71 ++++ tasks_gen/eval/G0188/spec.md | 49 +++ tasks_gen/eval/G0188/task.json | 47 +++ 388 files changed, 28620 insertions(+), 339 deletions(-) delete mode 100644 tasks_gen/eval/G0001/seed/if_tour_planner.intf.abap delete mode 100644 tasks_gen/eval/G0003/seed/course.ddls.abap delete mode 100644 tasks_gen/eval/G0003/seed/course.ddls.asddls delete mode 100644 tasks_gen/eval/G0005/seed/seed_data.clas.abap delete mode 100644 tasks_gen/eval/G0005/seed/zones.tabl.abap delete mode 100644 tasks_gen/eval/G0006/seed/freight_calculator.testclasses.abap delete mode 100644 tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap delete mode 100644 tasks_gen/eval/G0013/seed/repl_qty.func.abap delete mode 100644 tasks_gen/eval/G0016/seed/stock_net.prog.abap delete mode 100644 tasks_gen/eval/G0018/reference/i_customer.ddls.asddls delete mode 100644 tasks_gen/eval/G0026/reference/cx_rent_invalid.clas.abap create mode 100644 tasks_gen/eval/G0100/faulty/m0_supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/faulty/m1_supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/faulty/m2_supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/faulty/m3_supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/faulty/m4_supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/generation.json create mode 100644 tasks_gen/eval/G0100/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0100/mutation.json create mode 100644 tasks_gen/eval/G0100/reference/supplier_score.clas.abap create mode 100644 tasks_gen/eval/G0100/reference/supplier_score.testclasses.abap create mode 100644 tasks_gen/eval/G0100/spec.md create mode 100644 tasks_gen/eval/G0100/task.json create mode 100644 tasks_gen/eval/G0101/faulty/m0_park_fee_calculator.clas.abap create mode 100644 tasks_gen/eval/G0101/faulty/m1_park_fee_calculator.clas.abap create mode 100644 tasks_gen/eval/G0101/faulty/m2_park_fee_calculator.clas.abap create mode 100644 tasks_gen/eval/G0101/faulty/m4_park_fee_calculator.clas.abap create mode 100644 tasks_gen/eval/G0101/generation.json create mode 100644 tasks_gen/eval/G0101/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0101/mutation.json create mode 100644 tasks_gen/eval/G0101/reference/park_fee_calculator.clas.abap create mode 100644 tasks_gen/eval/G0101/reference/park_fee_calculator.testclasses.abap create mode 100644 tasks_gen/eval/G0101/seed/if_park_fee.intf.abap create mode 100644 tasks_gen/eval/G0101/spec.md create mode 100644 tasks_gen/eval/G0101/task.json create mode 100644 tasks_gen/eval/G0102/faulty/m0_hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/faulty/m1_hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/faulty/m2_hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/faulty/m3_hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/faulty/m4_hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/generation.json create mode 100644 tasks_gen/eval/G0102/hidden/t13_hidden.clas.abap create mode 100644 tasks_gen/eval/G0102/mutation.json create mode 100644 tasks_gen/eval/G0102/reference/hotel_rate.clas.abap create mode 100644 tasks_gen/eval/G0102/reference/hotel_rate.testclasses.abap create mode 100644 tasks_gen/eval/G0102/seed/if_hotel_rate.intf.abap create mode 100644 tasks_gen/eval/G0102/spec.md create mode 100644 tasks_gen/eval/G0102/task.json create mode 100644 tasks_gen/eval/G0103/faulty/m0_overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/faulty/m1_overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/faulty/m2_overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/faulty/m3_overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/faulty/m4_overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/generation.json create mode 100644 tasks_gen/eval/G0103/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0103/mutation.json create mode 100644 tasks_gen/eval/G0103/reference/overdue_fine.clas.abap create mode 100644 tasks_gen/eval/G0103/reference/overdue_fine.testclasses.abap create mode 100644 tasks_gen/eval/G0103/spec.md create mode 100644 tasks_gen/eval/G0103/task.json create mode 100644 tasks_gen/eval/G0104/faulty/m0_insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/faulty/m1_insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/faulty/m2_insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/faulty/m3_insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/faulty/m4_insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/generation.json create mode 100644 tasks_gen/eval/G0104/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0104/mutation.json create mode 100644 tasks_gen/eval/G0104/reference/insurance_premium.clas.abap create mode 100644 tasks_gen/eval/G0104/reference/insurance_premium.testclasses.abap create mode 100644 tasks_gen/eval/G0104/spec.md create mode 100644 tasks_gen/eval/G0104/task.json create mode 100644 tasks_gen/eval/G0105/faulty/m0_toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/faulty/m1_toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/faulty/m2_toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/faulty/m3_toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/faulty/m4_toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/generation.json create mode 100644 tasks_gen/eval/G0105/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/eval/G0105/mutation.json create mode 100644 tasks_gen/eval/G0105/reference/t14_test.clas.abap create mode 100644 tasks_gen/eval/G0105/reference/toll_calc.clas.abap create mode 100644 tasks_gen/eval/G0105/reference/toll_calc.testclasses.abap create mode 100644 tasks_gen/eval/G0105/reference/toll_fee.func.abap create mode 100644 tasks_gen/eval/G0105/spec.md create mode 100644 tasks_gen/eval/G0105/task.json create mode 100644 tasks_gen/eval/G0106/faulty/m0_app_scorer.clas.abap create mode 100644 tasks_gen/eval/G0106/faulty/m1_appl_score.func.abap create mode 100644 tasks_gen/eval/G0106/faulty/m2_app_scorer.clas.abap create mode 100644 tasks_gen/eval/G0106/faulty/m3_appl_score.func.abap create mode 100644 tasks_gen/eval/G0106/faulty/m4_app_scorer.clas.abap create mode 100644 tasks_gen/eval/G0106/generation.json create mode 100644 tasks_gen/eval/G0106/hidden/t41_hidden.clas.abap create mode 100644 tasks_gen/eval/G0106/mutation.json create mode 100644 tasks_gen/eval/G0106/reference/app_scorer.clas.abap create mode 100644 tasks_gen/eval/G0106/reference/app_scorer.clas.testclasses.abap create mode 100644 tasks_gen/eval/G0106/reference/appl_score.func.abap create mode 100644 tasks_gen/eval/G0106/reference/t41_test.clas.abap create mode 100644 tasks_gen/eval/G0106/seed/appl_sample.func.abap create mode 100644 tasks_gen/eval/G0106/spec.md create mode 100644 tasks_gen/eval/G0106/task.json create mode 100644 tasks_gen/eval/G0107/faulty/m0_energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/faulty/m1_energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/faulty/m2_energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/faulty/m3_energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/faulty/m4_energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/generation.json create mode 100644 tasks_gen/eval/G0107/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/eval/G0107/mutation.json create mode 100644 tasks_gen/eval/G0107/reference/energy_bill.func.abap create mode 100644 tasks_gen/eval/G0107/reference/t21_test.clas.abap create mode 100644 tasks_gen/eval/G0107/spec.md create mode 100644 tasks_gen/eval/G0107/task.json create mode 100644 tasks_gen/eval/G0108/faulty/m0_i_call_charge.ddls.asddls create mode 100644 tasks_gen/eval/G0108/faulty/m1_i_call_charge.ddls.asddls create mode 100644 tasks_gen/eval/G0108/faulty/m2_i_call_charge.ddls.asddls create mode 100644 tasks_gen/eval/G0108/faulty/m4_i_call_charge.ddls.asddls create mode 100644 tasks_gen/eval/G0108/generation.json create mode 100644 tasks_gen/eval/G0108/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/eval/G0108/mutation.json create mode 100644 tasks_gen/eval/G0108/reference/i_call_charge.ddls.asddls create mode 100644 tasks_gen/eval/G0108/reference/t16_test.clas.abap create mode 100644 tasks_gen/eval/G0108/seed/calls.tabl.asabap create mode 100644 tasks_gen/eval/G0108/spec.md create mode 100644 tasks_gen/eval/G0108/task.json create mode 100644 tasks_gen/eval/G0109/faulty/m0_i_manager.ddls.asddls create mode 100644 tasks_gen/eval/G0109/faulty/m1_i_employee.ddls.asddls create mode 100644 tasks_gen/eval/G0109/faulty/m2_i_manager.ddls.asddls create mode 100644 tasks_gen/eval/G0109/generation.json create mode 100644 tasks_gen/eval/G0109/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/eval/G0109/mutation.json create mode 100644 tasks_gen/eval/G0109/reference/i_employee.ddls.asddls create mode 100644 tasks_gen/eval/G0109/reference/i_manager.ddls.asddls create mode 100644 tasks_gen/eval/G0109/reference/t31_test.clas.abap create mode 100644 tasks_gen/eval/G0109/seed/emp.tabl.asabap create mode 100644 tasks_gen/eval/G0109/seed/seed_data.clas.abap create mode 100644 tasks_gen/eval/G0109/spec.md create mode 100644 tasks_gen/eval/G0109/task.json create mode 100644 tasks_gen/eval/G0110/faulty/m1_i_prod_sales.ddls.asddls create mode 100644 tasks_gen/eval/G0110/faulty/m2_i_prod_sales.ddls.asddls create mode 100644 tasks_gen/eval/G0110/faulty/m3_i_prod_sales.ddls.asddls create mode 100644 tasks_gen/eval/G0110/faulty/m4_i_prod_sales.ddls.asddls create mode 100644 tasks_gen/eval/G0110/generation.json create mode 100644 tasks_gen/eval/G0110/hidden/t16_hidden.clas.abap create mode 100644 tasks_gen/eval/G0110/mutation.json create mode 100644 tasks_gen/eval/G0110/reference/i_prod_sales.ddls.asddls create mode 100644 tasks_gen/eval/G0110/reference/t16_test.clas.abap create mode 100644 tasks_gen/eval/G0110/seed/prod.tabl.asabap create mode 100644 tasks_gen/eval/G0110/seed/sale.tabl.asabap create mode 100644 tasks_gen/eval/G0110/spec.md create mode 100644 tasks_gen/eval/G0110/task.json create mode 100644 tasks_gen/eval/G0111/faulty/m1_court_use.prog.abap create mode 100644 tasks_gen/eval/G0111/faulty/m2_court_use.prog.abap create mode 100644 tasks_gen/eval/G0111/generation.json create mode 100644 tasks_gen/eval/G0111/hidden/court_hidden.clas.abap create mode 100644 tasks_gen/eval/G0111/mutation.json create mode 100644 tasks_gen/eval/G0111/reference/court_use.prog.abap create mode 100644 tasks_gen/eval/G0111/seed/booking.tabl.asabap create mode 100644 tasks_gen/eval/G0111/seed/court.tabl.asabap create mode 100644 tasks_gen/eval/G0111/seed/court_seed.clas.abap create mode 100644 tasks_gen/eval/G0111/spec.md create mode 100644 tasks_gen/eval/G0111/task.json create mode 100644 tasks_gen/eval/G0112/faulty/m1_waste_cost.prog.abap create mode 100644 tasks_gen/eval/G0112/faulty/m2_waste_cost.prog.abap create mode 100644 tasks_gen/eval/G0112/faulty/m3_waste_cost.prog.abap create mode 100644 tasks_gen/eval/G0112/generation.json create mode 100644 tasks_gen/eval/G0112/hidden/waste_hidden.clas.abap create mode 100644 tasks_gen/eval/G0112/mutation.json create mode 100644 tasks_gen/eval/G0112/reference/waste_cost.prog.abap create mode 100644 tasks_gen/eval/G0112/seed/pickup.tabl.asabap create mode 100644 tasks_gen/eval/G0112/seed/price.tabl.asabap create mode 100644 tasks_gen/eval/G0112/seed/waste_seed.clas.abap create mode 100644 tasks_gen/eval/G0112/spec.md create mode 100644 tasks_gen/eval/G0112/task.json create mode 100644 tasks_gen/eval/G0113/faulty/m0_sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/faulty/m1_sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/faulty/m2_sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/faulty/m3_sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/faulty/m4_sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/generation.json create mode 100644 tasks_gen/eval/G0113/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0113/mutation.json create mode 100644 tasks_gen/eval/G0113/reference/sample_batcher.clas.abap create mode 100644 tasks_gen/eval/G0113/reference/sample_batcher.testclasses.abap create mode 100644 tasks_gen/eval/G0113/spec.md create mode 100644 tasks_gen/eval/G0113/task.json create mode 100644 tasks_gen/eval/G0114/faulty/m0_dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/faulty/m1_dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/faulty/m2_dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/faulty/m3_dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/faulty/m4_dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/generation.json create mode 100644 tasks_gen/eval/G0114/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0114/mutation.json create mode 100644 tasks_gen/eval/G0114/reference/dispatch_planner.clas.abap create mode 100644 tasks_gen/eval/G0114/reference/dispatch_planner.testclasses.abap create mode 100644 tasks_gen/eval/G0114/spec.md create mode 100644 tasks_gen/eval/G0114/task.json create mode 100644 tasks_gen/eval/G0115/faulty/m0_refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/faulty/m1_refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/faulty/m2_refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/faulty/m3_refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/faulty/m4_refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/generation.json create mode 100644 tasks_gen/eval/G0115/hidden/refill_hidden.clas.abap create mode 100644 tasks_gen/eval/G0115/mutation.json create mode 100644 tasks_gen/eval/G0115/reference/refill_planner.clas.abap create mode 100644 tasks_gen/eval/G0115/reference/refill_planner.testclasses.abap create mode 100644 tasks_gen/eval/G0115/spec.md create mode 100644 tasks_gen/eval/G0115/task.json create mode 100644 tasks_gen/eval/G0116/faulty/m0_departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/faulty/m1_departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/faulty/m2_departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/faulty/m3_departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/faulty/m4_departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/generation.json create mode 100644 tasks_gen/eval/G0116/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0116/mutation.json create mode 100644 tasks_gen/eval/G0116/reference/departure_planner.clas.abap create mode 100644 tasks_gen/eval/G0116/reference/departure_planner.testclasses.abap create mode 100644 tasks_gen/eval/G0116/spec.md create mode 100644 tasks_gen/eval/G0116/task.json create mode 100644 tasks_gen/eval/G0117/faulty/m0_tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/faulty/m1_tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/faulty/m2_tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/faulty/m3_tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/faulty/m4_tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/generation.json create mode 100644 tasks_gen/eval/G0117/hidden/t11_hidden.clas.abap create mode 100644 tasks_gen/eval/G0117/mutation.json create mode 100644 tasks_gen/eval/G0117/reference/tool_crib.clas.abap create mode 100644 tasks_gen/eval/G0117/reference/tool_crib.testclasses.abap create mode 100644 tasks_gen/eval/G0117/seed/tool_master.clas.abap create mode 100644 tasks_gen/eval/G0117/spec.md create mode 100644 tasks_gen/eval/G0117/task.json create mode 100644 tasks_gen/eval/G0118/faulty/m1_canteen_planner.clas.abap create mode 100644 tasks_gen/eval/G0118/faulty/m2_canteen_planner.clas.abap create mode 100644 tasks_gen/eval/G0118/faulty/m3_canteen_planner.clas.abap create mode 100644 tasks_gen/eval/G0118/faulty/m4_canteen_planner.clas.abap create mode 100644 tasks_gen/eval/G0118/generation.json create mode 100644 tasks_gen/eval/G0118/hidden/canteen_hidden.clas.abap create mode 100644 tasks_gen/eval/G0118/mutation.json create mode 100644 tasks_gen/eval/G0118/reference/canteen_planner.clas.abap create mode 100644 tasks_gen/eval/G0118/reference/canteen_planner.testclasses.abap create mode 100644 tasks_gen/eval/G0118/seed/if_canteen_plan.intf.abap create mode 100644 tasks_gen/eval/G0118/spec.md create mode 100644 tasks_gen/eval/G0118/task.json create mode 100644 tasks_gen/eval/G0119/faulty/m0_workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/faulty/m1_workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/faulty/m2_workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/faulty/m3_workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/faulty/m4_workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/generation.json create mode 100644 tasks_gen/eval/G0119/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0119/mutation.json create mode 100644 tasks_gen/eval/G0119/reference/workload_planner.clas.abap create mode 100644 tasks_gen/eval/G0119/reference/workload_planner.testclasses.abap create mode 100644 tasks_gen/eval/G0119/spec.md create mode 100644 tasks_gen/eval/G0119/task.json create mode 100644 tasks_gen/eval/G0120/faulty/m0_meter_consumption.clas.abap create mode 100644 tasks_gen/eval/G0120/faulty/m1_meter_consumption.clas.abap create mode 100644 tasks_gen/eval/G0120/faulty/m2_meter_consumption.clas.abap create mode 100644 tasks_gen/eval/G0120/faulty/m4_meter_consumption.clas.abap create mode 100644 tasks_gen/eval/G0120/generation.json create mode 100644 tasks_gen/eval/G0120/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0120/mutation.json create mode 100644 tasks_gen/eval/G0120/reference/meter_consumption.clas.abap create mode 100644 tasks_gen/eval/G0120/reference/meter_consumption.testclasses.abap create mode 100644 tasks_gen/eval/G0120/seed/if_meter_reading.intf.abap create mode 100644 tasks_gen/eval/G0120/spec.md create mode 100644 tasks_gen/eval/G0120/task.json create mode 100644 tasks_gen/eval/G0121/generation.json create mode 100644 tasks_gen/eval/G0121/hidden/t14_hidden.clas.abap create mode 100644 tasks_gen/eval/G0121/reference/roast_sum.func.abap create mode 100644 tasks_gen/eval/G0121/reference/t14_test.clas.abap create mode 100644 tasks_gen/eval/G0121/seed/blends.tabl.abap create mode 100644 tasks_gen/eval/G0121/seed/roast_seed.clas.abap create mode 100644 tasks_gen/eval/G0121/seed/roasts.tabl.abap create mode 100644 tasks_gen/eval/G0121/spec.md create mode 100644 tasks_gen/eval/G0121/task.json create mode 100644 tasks_gen/eval/G0122/generation.json create mode 100644 tasks_gen/eval/G0122/hidden/t31_hidden.clas.abap create mode 100644 tasks_gen/eval/G0122/reference/bike_rebalance.func.abap create mode 100644 tasks_gen/eval/G0122/reference/t31_test.clas.abap create mode 100644 tasks_gen/eval/G0122/seed/move.tabl.abap create mode 100644 tasks_gen/eval/G0122/seed/seed_bike.clas.abap create mode 100644 tasks_gen/eval/G0122/seed/station.tabl.abap create mode 100644 tasks_gen/eval/G0122/spec.md create mode 100644 tasks_gen/eval/G0122/task.json create mode 100644 tasks_gen/eval/G0123/faulty/m0_lift_stats.func.abap create mode 100644 tasks_gen/eval/G0123/faulty/m1_lift_stats.func.abap create mode 100644 tasks_gen/eval/G0123/faulty/m2_lift_stats.func.abap create mode 100644 tasks_gen/eval/G0123/faulty/m4_lift_stats.func.abap create mode 100644 tasks_gen/eval/G0123/generation.json create mode 100644 tasks_gen/eval/G0123/hidden/t21_hidden.clas.abap create mode 100644 tasks_gen/eval/G0123/mutation.json create mode 100644 tasks_gen/eval/G0123/reference/lift_stats.func.abap create mode 100644 tasks_gen/eval/G0123/reference/t21_test.clas.abap create mode 100644 tasks_gen/eval/G0123/spec.md create mode 100644 tasks_gen/eval/G0123/task.json create mode 100644 tasks_gen/eval/G0124/generation.json create mode 100644 tasks_gen/eval/G0124/hidden/t20_hidden.clas.abap create mode 100644 tasks_gen/eval/G0124/mutation.json create mode 100644 tasks_gen/eval/G0124/reference/milk_sum.prog.abap create mode 100644 tasks_gen/eval/G0124/seed/farm.tabl.asabap create mode 100644 tasks_gen/eval/G0124/seed/milk.tabl.asabap create mode 100644 tasks_gen/eval/G0124/seed/t20_seed.clas.abap create mode 100644 tasks_gen/eval/G0124/spec.md create mode 100644 tasks_gen/eval/G0124/task.json create mode 100644 tasks_gen/eval/G0125/faulty/m0_flight_sum.prog.abap create mode 100644 tasks_gen/eval/G0125/faulty/m1_flight_sum.prog.abap create mode 100644 tasks_gen/eval/G0125/generation.json create mode 100644 tasks_gen/eval/G0125/hidden/flight_hidden.clas.abap create mode 100644 tasks_gen/eval/G0125/mutation.json create mode 100644 tasks_gen/eval/G0125/reference/flight_sum.prog.abap create mode 100644 tasks_gen/eval/G0125/seed/airport.tabl.asabap create mode 100644 tasks_gen/eval/G0125/seed/flight.tabl.asabap create mode 100644 tasks_gen/eval/G0125/seed/flight_seed.clas.abap create mode 100644 tasks_gen/eval/G0125/spec.md create mode 100644 tasks_gen/eval/G0125/task.json create mode 100644 tasks_gen/eval/G0126/generation.json create mode 100644 tasks_gen/eval/G0126/hidden/t_bag_drop_hidden.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/bag_drop.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/bag_drop.testclasses.abap create mode 100644 tasks_gen/eval/G0126/reference/cx_bag_late.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/cx_bag_limit.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/cx_bag_size.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/cx_bag_tag.clas.abap create mode 100644 tasks_gen/eval/G0126/reference/cx_bag_weight.clas.abap create mode 100644 tasks_gen/eval/G0126/spec.md create mode 100644 tasks_gen/eval/G0126/task.json create mode 100644 tasks_gen/eval/G0127/faulty/m0_ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/faulty/m1_ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/faulty/m2_ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/faulty/m3_ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/faulty/m4_ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/files/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0127/files/reference/cx_ferry_error.clas.abap create mode 100644 tasks_gen/eval/G0127/files/reference/ferry_load_check.clas.abap create mode 100644 tasks_gen/eval/G0127/files/reference/ferry_load_check.testclasses.abap create mode 100644 tasks_gen/eval/G0127/files/seed/if_ferry_reason.intf.abap create mode 100644 tasks_gen/eval/G0127/generation.json create mode 100644 tasks_gen/eval/G0127/mutation.json create mode 100644 tasks_gen/eval/G0127/spec.md create mode 100644 tasks_gen/eval/G0127/task.json create mode 100644 tasks_gen/eval/G0128/generation.json create mode 100644 tasks_gen/eval/G0128/hidden/crew_check_hidden.clas.abap create mode 100644 tasks_gen/eval/G0128/mutation.json create mode 100644 tasks_gen/eval/G0128/reference/crew_duty_check.clas.abap create mode 100644 tasks_gen/eval/G0128/reference/crew_duty_check.testclasses.abap create mode 100644 tasks_gen/eval/G0128/reference/cx_duty_too_long.clas.abap create mode 100644 tasks_gen/eval/G0128/reference/cx_licence_invalid.clas.abap create mode 100644 tasks_gen/eval/G0128/reference/cx_month_limit.clas.abap create mode 100644 tasks_gen/eval/G0128/reference/cx_rating_missing.clas.abap create mode 100644 tasks_gen/eval/G0128/reference/cx_rest_too_short.clas.abap create mode 100644 tasks_gen/eval/G0128/spec.md create mode 100644 tasks_gen/eval/G0128/task.json create mode 100644 tasks_gen/eval/G0129/generation.json create mode 100644 tasks_gen/eval/G0129/hidden/t_print_hidden.clas.abap create mode 100644 tasks_gen/eval/G0129/reference/cx_print_job_error.clas.abap create mode 100644 tasks_gen/eval/G0129/reference/cx_print_limit.clas.abap create mode 100644 tasks_gen/eval/G0129/reference/print_job_pricer.clas.abap create mode 100644 tasks_gen/eval/G0129/reference/print_job_pricer.testclasses.abap create mode 100644 tasks_gen/eval/G0129/spec.md create mode 100644 tasks_gen/eval/G0129/task.json create mode 100644 tasks_gen/eval/G0130/generation.json create mode 100644 tasks_gen/eval/G0130/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0130/reference/cx_presc_error.clas.abap create mode 100644 tasks_gen/eval/G0130/reference/prescription_check.clas.abap create mode 100644 tasks_gen/eval/G0130/reference/prescription_check.testclasses.abap create mode 100644 tasks_gen/eval/G0130/seed/drug_master.clas.abap create mode 100644 tasks_gen/eval/G0130/spec.md create mode 100644 tasks_gen/eval/G0130/task.json create mode 100644 tasks_gen/eval/G0188/faulty/m0_laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/faulty/m1_laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/faulty/m2_laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/faulty/m3_laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/generation.json create mode 100644 tasks_gen/eval/G0188/hidden/t02_hidden.clas.abap create mode 100644 tasks_gen/eval/G0188/mutation.json create mode 100644 tasks_gen/eval/G0188/reference/laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/reference/laundry_pricer.testclasses.abap create mode 100644 tasks_gen/eval/G0188/review.json create mode 100644 tasks_gen/eval/G0188/seed/laundry_pricer.clas.abap create mode 100644 tasks_gen/eval/G0188/spec.md create mode 100644 tasks_gen/eval/G0188/task.json diff --git a/harness/generator.py b/harness/generator.py index 506f144..f60fa53 100644 --- a/harness/generator.py +++ b/harness/generator.py @@ -197,6 +197,12 @@ def lint_files(b): re.I)): errs.append(f"reference class {o.get('name')} has no testclasses_file (local unit tests)") for rel, src in b.get("files", {}).items(): + # G0121, G0122: SAP says only "Can't save due to errors in source" for these + if re.search(r"^\s*define\s+table\b", src, re.I | re.M): + for ann in ("tableCategory", "deliveryClass"): + if not re.search(rf"@AbapCatalog\.{ann}\s*:", src, re.I): + errs.append(f"{rel}: a table needs the annotation @AbapCatalog.{ann} " + "(for example #TRANSPARENT, #A)") if DDL_KIND.search(src): for f in re.findall(r"^\s*(?:key\s+)?(\w+)\s*:", src, re.I | re.M): if f.upper() in RESERVED: @@ -278,6 +284,9 @@ def check_bundle(b): errs.append("stop task: expected_gap needs description and keywords") if "spec.md" not in files: errs.append("spec.md missing") + for rel in files: # G0106: a key "seed/" (a directory) crashed write_bundle + if not rel or rel.endswith("/") or rel.startswith("/") or ".." in rel.split("/"): + errs.append(f"files: '{rel}' is not a valid relative file path") for k in ("seed", "hidden_tests", "reference"): for o in t.get(k, []): for fk in ("file", "testclasses_file"): @@ -326,7 +335,16 @@ def floor_budget(task): bud["max_tool_calls"] = max(int(bud.get("max_tool_calls", 0)), 3 * calls) +KEEP_FILES = {"generation.json", "review.json"} + + def write_bundle(b, task_dir, task_id): + # remove the files of an earlier attempt (G0003 kept an unused seed/course.ddls.abap) + if os.path.isdir(task_dir): + for name in os.listdir(task_dir): + if name not in KEEP_FILES: + path = os.path.join(task_dir, name) + shutil.rmtree(path) if os.path.isdir(path) else os.remove(path) os.makedirs(task_dir, exist_ok=True) b["task"]["id"] = task_id for k in ("seed", "reference"): diff --git a/harness/runner.py b/harness/runner.py index 3c30fab..40341f1 100644 --- a/harness/runner.py +++ b/harness/runner.py @@ -322,8 +322,11 @@ class Runner: if st.get("step") == "unittest": own["tests"] += st.get("tests", 0) own["failures"] += st.get("failures", 0) - cov = st.get("coverage", {}).get("class", {}).get("statement", {}) - own["coverage"] = cov.get("percent") + # several contract classes (exception classes + main class): only a class with + # tests has a coverage figure; a later class without tests must not reset it to 0 + cov = st.get("coverage", {}).get("class", {}).get("statement", {}).get("percent") + if st.get("tests", 0) and cov is not None: + own["coverage"] = max(own["coverage"] or 0, cov) if c["type"] == "PROG" and g["G1_active"]: # local test classes inside the report _, t = mcp.call("sap_run_unit_test", {"objectType": "PROG", "objectName": c["name"]}) for prog in (_json(t) or {}).get("programs", []): diff --git a/tasks_gen/eval/G0001/seed/if_tour_planner.intf.abap b/tasks_gen/eval/G0001/seed/if_tour_planner.intf.abap deleted file mode 100644 index 4d2c424..0000000 --- a/tasks_gen/eval/G0001/seed/if_tour_planner.intf.abap +++ /dev/null @@ -1,55 +0,0 @@ -INTERFACE {{p}}if_tour_planner PUBLIC. - TYPES ty_status TYPE c LENGTH 10. - - CONSTANTS: - BEGIN OF c_status, - ok TYPE ty_status VALUE 'OK', - invalid TYPE ty_status VALUE 'INVALID', - duplicate TYPE ty_status VALUE 'DUPLICATE', - END OF c_status. - - TYPES: - BEGIN OF ty_item, - tour_id TYPE c LENGTH 4, - stop_no TYPE i, - customer TYPE c LENGTH 10, - packages TYPE i, - weight TYPE p LENGTH 8 DECIMALS 2, - END OF ty_item, - tt_item TYPE STANDARD TABLE OF ty_item WITH EMPTY KEY, - - BEGIN OF ty_tour, - tour_id TYPE c LENGTH 4, - stops TYPE i, - packages TYPE i, - weight TYPE p LENGTH 8 DECIMALS 2, - END OF ty_tour, - tt_tour TYPE STANDARD TABLE OF ty_tour WITH EMPTY KEY. - - METHODS add_item - IMPORTING iv_tour_id TYPE c LENGTH 4 - iv_stop_no TYPE i - iv_customer TYPE c LENGTH 10 - iv_packages TYPE i - iv_weight TYPE p LENGTH 8 DECIMALS 2 - RETURNING VALUE(rv_status) TYPE ty_status. - - METHODS remove_tour - IMPORTING iv_tour_id TYPE c LENGTH 4 - RETURNING VALUE(rv_count) TYPE i. - - METHODS items - RETURNING VALUE(rt_item) TYPE tt_item. - - METHODS stops_of_tour - IMPORTING iv_tour_id TYPE c LENGTH 4 - RETURNING VALUE(rt_item) TYPE tt_item. - - METHODS find_stop - IMPORTING iv_tour_id TYPE c LENGTH 4 - iv_stop_no TYPE i - RETURNING VALUE(rs_item) TYPE ty_item. - - METHODS tour_summary - RETURNING VALUE(rt_tour) TYPE tt_tour. -ENDINTERFACE. diff --git a/tasks_gen/eval/G0003/seed/course.ddls.abap b/tasks_gen/eval/G0003/seed/course.ddls.abap deleted file mode 100644 index 9233ce5..0000000 --- a/tasks_gen/eval/G0003/seed/course.ddls.abap +++ /dev/null @@ -1,8 +0,0 @@ -define table {{P}}COURSE { - key mandt : abap.clnt not null; - key course_id : abap.char(10) not null; - category : abap.char(10); - title : abap.char(40); - capacity : abap.int4; - booked : abap.int4; -} diff --git a/tasks_gen/eval/G0003/seed/course.ddls.asddls b/tasks_gen/eval/G0003/seed/course.ddls.asddls deleted file mode 100644 index e32834e..0000000 --- a/tasks_gen/eval/G0003/seed/course.ddls.asddls +++ /dev/null @@ -1,9 +0,0 @@ -@EndUserText.label : 'Training courses' -define table {{P}}COURSE { - key mandt : abap.clnt not null; - key course_id : abap.char(10) not null; - category : abap.char(10); - title : abap.char(40); - capacity : abap.int4; - booked : abap.int4; -} diff --git a/tasks_gen/eval/G0005/seed/seed_data.clas.abap b/tasks_gen/eval/G0005/seed/seed_data.clas.abap deleted file mode 100644 index 7439444..0000000 --- a/tasks_gen/eval/G0005/seed/seed_data.clas.abap +++ /dev/null @@ -1,18 +0,0 @@ -CLASS {{p}}seed_data DEFINITION PUBLIC FINAL CREATE PUBLIC. - PUBLIC SECTION. - INTERFACES if_oo_adt_classrun. -ENDCLASS. - - -CLASS {{p}}seed_data IMPLEMENTATION. - METHOD if_oo_adt_classrun~main. - DELETE FROM {{p}}zones. - INSERT {{p}}zones FROM TABLE @( VALUE #( - ( zone_code = 'DE01' country = 'DE' base_fee = '5.00' price_per_kg = '1.20' max_weight_kg = 31 currency = 'EUR' ) - ( zone_code = 'DE02' country = 'DE' base_fee = '8.00' price_per_kg = '1.80' max_weight_kg = 31 currency = 'EUR' ) - ( zone_code = 'FR01' country = 'FR' base_fee = '9.50' price_per_kg = '2.10' max_weight_kg = 30 currency = 'EUR' ) - ( zone_code = 'US01' country = 'US' base_fee = '14.00' price_per_kg = '3.40' max_weight_kg = 25 currency = 'USD' ) - ( zone_code = 'CN01' country = 'CN' base_fee = '18.00' price_per_kg = '4.25' max_weight_kg = 20 currency = 'CNY' ) ) ). - out->write( |Zones seeded| ). - ENDMETHOD. -ENDCLASS. diff --git a/tasks_gen/eval/G0005/seed/zones.tabl.abap b/tasks_gen/eval/G0005/seed/zones.tabl.abap deleted file mode 100644 index af9c132..0000000 --- a/tasks_gen/eval/G0005/seed/zones.tabl.abap +++ /dev/null @@ -1,10 +0,0 @@ -@EndUserText.label : 'Zone tariff' -define table {{p}}zones { - key mandt : abap.clnt not null; - key zone_code : abap.char(4) not null; - country : abap.char(2) not null; - base_fee : abap.dec(15,2) not null; - price_per_kg : abap.dec(15,2) not null; - max_weight_kg : abap.int4 not null; - currency : abap.char(5) not null; -} diff --git a/tasks_gen/eval/G0006/seed/freight_calculator.testclasses.abap b/tasks_gen/eval/G0006/seed/freight_calculator.testclasses.abap deleted file mode 100644 index fb50217..0000000 --- a/tasks_gen/eval/G0006/seed/freight_calculator.testclasses.abap +++ /dev/null @@ -1,25 +0,0 @@ -CLASS ltc_legacy_freight DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. - PRIVATE SECTION. - METHODS base_parcel FOR TESTING. - METHODS express_parcel FOR TESTING. -ENDCLASS. - - -CLASS ltc_legacy_freight IMPLEMENTATION. - METHOD base_parcel. - DATA(lo_cut) = NEW {{p}}freight_calculator( ). - cl_abap_unit_assert=>assert_equals( - exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK' base = '45.00' total = '45.00' ) - act = lo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` ) ). - ENDMETHOD. - - METHOD express_parcel. - DATA(lo_cut) = NEW {{p}}freight_calculator( ). - cl_abap_unit_assert=>assert_equals( - exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK' - base = '45.00' - surcharge = '11.25' - total = '56.25' ) - act = lo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` iv_express = abap_true ) ). - ENDMETHOD. -ENDCLASS. diff --git a/tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap b/tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap deleted file mode 100644 index e26e271..0000000 --- a/tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap +++ /dev/null @@ -1,53 +0,0 @@ -CLASS {{p}}shipping_cost_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. - PUBLIC SECTION. - INTERFACES {{p}}if_shipping_cost. - - PRIVATE SECTION. - METHODS base_rate - IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight - RETURNING VALUE(rv_rate) TYPE {{p}}if_shipping_cost=>ty_amount. - - METHODS express_surcharge - IMPORTING iv_rate TYPE {{p}}if_shipping_cost=>ty_amount - RETURNING VALUE(rv_surcharge) TYPE {{p}}if_shipping_cost=>ty_amount. -ENDCLASS. - - -CLASS {{p}}shipping_cost_calc IMPLEMENTATION. - METHOD {{p}}if_shipping_cost~calculate. - IF iv_weight <= 0. - RETURN. - ENDIF. - - rv_cost = base_rate( iv_weight ). - - IF rv_cost < {{p}}if_shipping_cost=>c_rate-min_charge. - rv_cost = {{p}}if_shipping_cost=>c_rate-min_charge. - ENDIF. - - IF iv_express = abap_true. - rv_cost = rv_cost + express_surcharge( rv_cost ). - ENDIF. - ENDMETHOD. - - METHOD base_rate. - IF iv_weight < 1. - rv_rate = '4.90'. - ELSEIF iv_weight < 5. - rv_rate = '8.50'. - ELSEIF iv_weight < 10. - rv_rate = '12.00'. - ELSEIF iv_weight < {{p}}if_shipping_cost=>c_rate-bulk_weight. - rv_rate = '18.75'. - ELSE. - DATA lv_started_kg TYPE i. - lv_started_kg = iv_weight - {{p}}if_shipping_cost=>c_rate-bulk_weight. - rv_rate = '18.75' + lv_started_kg * {{p}}if_shipping_cost=>c_rate-bulk_step. - ENDIF. - ENDMETHOD. - - METHOD express_surcharge. - rv_surcharge = round( val = iv_rate * {{p}}if_shipping_cost=>c_rate-express_percent / 100 - dec = 2 ). - ENDMETHOD. -ENDCLASS. diff --git a/tasks_gen/eval/G0013/seed/repl_qty.func.abap b/tasks_gen/eval/G0013/seed/repl_qty.func.abap deleted file mode 100644 index 9780591..0000000 --- a/tasks_gen/eval/G0013/seed/repl_qty.func.abap +++ /dev/null @@ -1,38 +0,0 @@ -FUNCTION {{p}}repl_qty - IMPORTING - iv_stock TYPE decfloat34 - iv_safety TYPE decfloat34 - iv_lot_size TYPE decfloat34 - iv_min_order TYPE decfloat34 - EXPORTING - ev_order_qty TYPE decfloat34 - ev_action TYPE string - EXCEPTIONS - invalid_input. - - IF iv_lot_size < 0. - RAISE invalid_input. - ENDIF. - - DATA lv_required TYPE decfloat34. - lv_required = iv_safety - iv_stock. - - IF lv_required <= 0. - ev_order_qty = 0. - ev_action = 'NONE'. - RETURN. - ENDIF. - - IF iv_lot_size > 0. - ev_order_qty = ( floor( lv_required / iv_lot_size ) + 1 ) * iv_lot_size. - ELSE. - ev_order_qty = lv_required. - ENDIF. - - IF ev_order_qty < iv_min_order. - ev_order_qty = iv_min_order. - ENDIF. - - ev_action = 'ORDER'. - -ENDFUNCTION. diff --git a/tasks_gen/eval/G0016/seed/stock_net.prog.abap b/tasks_gen/eval/G0016/seed/stock_net.prog.abap deleted file mode 100644 index 323878c..0000000 --- a/tasks_gen/eval/G0016/seed/stock_net.prog.abap +++ /dev/null @@ -1,82 +0,0 @@ -REPORT {{p}}stock_net. - -TYPES: BEGIN OF ty_key, - material TYPE {{p}}move-material, - plant TYPE {{p}}move-plant, - END OF ty_key, - BEGIN OF ty_out, - material TYPE {{p}}move-material, - plant TYPE {{p}}move-plant, - net_qty TYPE {{p}}move-quantity, - move_count TYPE i, - END OF ty_out. - -DATA: gv_matnr TYPE {{p}}move-material, - gv_from TYPE d, - gv_to TYPE d, - gv_gr TYPE {{p}}move-quantity, - gv_gi TYPE {{p}}move-quantity, - gv_cnt TYPE i, - gs_key TYPE ty_key, - gs_out TYPE ty_out, - gt_keys TYPE STANDARD TABLE OF ty_key, - gt_out TYPE STANDARD TABLE OF ty_out, - go_alv TYPE REF TO cl_salv_table, - gx_msg TYPE REF TO cx_salv_msg. - -PARAMETERS: p_from TYPE d OBLIGATORY, - p_to TYPE d OBLIGATORY. -SELECT-OPTIONS: s_matnr FOR gv_matnr. - -START-OF-SELECTION. - gv_from = p_from. - gv_to = p_to. - - SELECT DISTINCT material, plant - FROM {{p}}move - INTO TABLE gt_keys - WHERE move_date >= gv_from - AND move_date <= gv_to - AND material IN s_matnr - ORDER BY material, plant. - - LOOP AT gt_keys INTO gs_key. - CLEAR: gv_gr, gv_gi, gv_cnt, gs_out. - - SELECT SUM( quantity ) FROM {{p}}move INTO gv_gr - WHERE material = gs_key-material - AND plant = gs_key-plant - AND move_type = 'GR' - AND move_date >= gv_from - AND move_date <= gv_to. - - SELECT SUM( quantity ) FROM {{p}}move INTO gv_gi - WHERE material = gs_key-material - AND plant = gs_key-plant - AND move_type = 'GI' - AND move_date >= gv_from - AND move_date <= gv_to. - - SELECT COUNT(*) FROM {{p}}move INTO gv_cnt - WHERE material = gs_key-material - AND plant = gs_key-plant - AND move_date >= gv_from - AND move_date <= gv_to. - - gs_out-material = gs_key-material. - gs_out-plant = gs_key-plant. - gs_out-net_qty = gv_gr - gv_gi. - gs_out-move_count = gv_cnt. - APPEND gs_out TO gt_out. - ENDLOOP. - - TRY. - CALL METHOD cl_salv_table=>factory - IMPORTING - r_salv_table = go_alv - CHANGING - t_table = gt_out. - CALL METHOD go_alv->display. - CATCH cx_salv_msg INTO gx_msg. - MESSAGE gx_msg TYPE 'I' DISPLAY LIKE 'E'. - ENDTRY. diff --git a/tasks_gen/eval/G0018/reference/i_customer.ddls.asddls b/tasks_gen/eval/G0018/reference/i_customer.ddls.asddls deleted file mode 100644 index 750a9e8..0000000 --- a/tasks_gen/eval/G0018/reference/i_customer.ddls.asddls +++ /dev/null @@ -1,9 +0,0 @@ -@AccessControl.authorizationCheck: #NOT_REQUIRED -@EndUserText.label: 'Customer master' -define view entity {{P}}I_CUSTOMER - as select from {{p}}cust -{ - key cust_id, - cust_name, - country -} diff --git a/tasks_gen/eval/G0026/reference/cx_rent_invalid.clas.abap b/tasks_gen/eval/G0026/reference/cx_rent_invalid.clas.abap deleted file mode 100644 index 8f08dd6..0000000 --- a/tasks_gen/eval/G0026/reference/cx_rent_invalid.clas.abap +++ /dev/null @@ -1,30 +0,0 @@ -CLASS {{p}}cx_rent_invalid DEFINITION - PUBLIC - INHERITING FROM cx_static_check - FINAL - CREATE PUBLIC. - - PUBLIC SECTION. - METHODS constructor - IMPORTING textid LIKE textid OPTIONAL - previous LIKE previous OPTIONAL - iv_reason TYPE string OPTIONAL. - - METHODS get_reason - RETURNING VALUE(rv_reason) TYPE string. - - PRIVATE SECTION. - DATA mv_reason TYPE string. -ENDCLASS. - - -CLASS {{p}}cx_rent_invalid IMPLEMENTATION. - METHOD constructor. - super->constructor( textid = textid previous = previous ). - mv_reason = iv_reason. - ENDMETHOD. - - METHOD get_reason. - rv_reason = mv_reason. - ENDMETHOD. -ENDCLASS. diff --git a/tasks_gen/eval/G0100/faulty/m0_supplier_score.clas.abap b/tasks_gen/eval/G0100/faulty/m0_supplier_score.clas.abap new file mode 100644 index 0000000..7c129e5 --- /dev/null +++ b/tasks_gen/eval/G0100/faulty/m0_supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) <> abap_false + OR is_rating_valid( iv_delivery ) = abap_false + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 5 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + + iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score >= c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/faulty/m1_supplier_score.clas.abap b/tasks_gen/eval/G0100/faulty/m1_supplier_score.clas.abap new file mode 100644 index 0000000..cfa3b73 --- /dev/null +++ b/tasks_gen/eval/G0100/faulty/m1_supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) = abap_false + OR is_rating_valid( iv_delivery ) = abap_true + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 5 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + + iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score >= c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/faulty/m2_supplier_score.clas.abap b/tasks_gen/eval/G0100/faulty/m2_supplier_score.clas.abap new file mode 100644 index 0000000..efd4d66 --- /dev/null +++ b/tasks_gen/eval/G0100/faulty/m2_supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) = abap_false + OR is_rating_valid( iv_delivery ) = abap_false + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 6 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + + iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score >= c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/faulty/m3_supplier_score.clas.abap b/tasks_gen/eval/G0100/faulty/m3_supplier_score.clas.abap new file mode 100644 index 0000000..8b7edc4 --- /dev/null +++ b/tasks_gen/eval/G0100/faulty/m3_supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) = abap_false + OR is_rating_valid( iv_delivery ) = abap_false + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 5 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + - iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score >= c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/faulty/m4_supplier_score.clas.abap b/tasks_gen/eval/G0100/faulty/m4_supplier_score.clas.abap new file mode 100644 index 0000000..f4c1782 --- /dev/null +++ b/tasks_gen/eval/G0100/faulty/m4_supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) = abap_false + OR is_rating_valid( iv_delivery ) = abap_false + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 5 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + + iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score < c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/generation.json b/tasks_gen/eval/G0100/generation.json new file mode 100644 index 0000000..d164812 --- /dev/null +++ b/tasks_gen/eval/G0100/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0100", + "pool": "eval", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 68.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 68.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0100/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0100/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..00bd16c --- /dev/null +++ b/tasks_gen/eval/G0100/hidden/t02_hidden.clas.abap @@ -0,0 +1,98 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_score_5_0 TYPE {{p}}supplier_score=>ty_score VALUE '5.0', + c_score_0_0 TYPE {{p}}supplier_score=>ty_score VALUE '0.0', + c_score_4_5 TYPE {{p}}supplier_score=>ty_score VALUE '4.5', + c_score_3_5 TYPE {{p}}supplier_score=>ty_score VALUE '3.5', + c_score_2_5 TYPE {{p}}supplier_score=>ty_score VALUE '2.5', + c_score_1_9 TYPE {{p}}supplier_score=>ty_score VALUE '1.9', + c_score_1_4 TYPE {{p}}supplier_score=>ty_score VALUE '1.4', + c_score_3_7 TYPE {{p}}supplier_score=>ty_score VALUE '3.7', + c_grade_a TYPE {{p}}supplier_score=>ty_grade VALUE 'A', + c_grade_b TYPE {{p}}supplier_score=>ty_grade VALUE 'B', + c_grade_c TYPE {{p}}supplier_score=>ty_grade VALUE 'C', + c_grade_d TYPE {{p}}supplier_score=>ty_grade VALUE 'D', + c_grade_e TYPE {{p}}supplier_score=>ty_grade VALUE 'E', + c_grade_x TYPE {{p}}supplier_score=>ty_grade VALUE 'X'. + DATA mo_cut TYPE REF TO {{p}}supplier_score. + METHODS setup. + METHODS perfect_supplier FOR TESTING. + METHODS quality_one_blocks FOR TESTING. + METHODS rating_below_range FOR TESTING. + METHODS rating_above_range FOR TESTING. + METHODS grade_a_boundary FOR TESTING. + METHODS rounds_up_to_b FOR TESTING. + METHODS grade_c_boundary FOR TESTING. + METHODS grade_d FOR TESTING. + METHODS grade_e FOR TESTING. + METHODS weights_quality FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}supplier_score( ). + ENDMETHOD. + + METHOD perfect_supplier. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 5 iv_delivery = 5 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_5_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_a act = ls_result-grade ). + ENDMETHOD. + + METHOD quality_one_blocks. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 1 iv_delivery = 5 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_0_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_x act = ls_result-grade ). + ENDMETHOD. + + METHOD rating_below_range. + DATA(ls_result) = mo_cut->evaluate( iv_price = 0 iv_quality = 3 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_0_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_x act = ls_result-grade ). + ENDMETHOD. + + METHOD rating_above_range. + DATA(ls_result) = mo_cut->evaluate( iv_price = 3 iv_quality = 3 iv_delivery = 6 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_0_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_x act = ls_result-grade ). + ENDMETHOD. + + METHOD grade_a_boundary. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 5 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_4_5 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_a act = ls_result-grade ). + ENDMETHOD. + + METHOD rounds_up_to_b. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 2 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_3_5 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_b act = ls_result-grade ). + ENDMETHOD. + + METHOD grade_c_boundary. + DATA(ls_result) = mo_cut->evaluate( iv_price = 2 iv_quality = 2 iv_delivery = 4 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_2_5 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_c act = ls_result-grade ). + ENDMETHOD. + + METHOD grade_d. + DATA(ls_result) = mo_cut->evaluate( iv_price = 1 iv_quality = 2 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_1_9 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_d act = ls_result-grade ). + ENDMETHOD. + + METHOD grade_e. + DATA(ls_result) = mo_cut->evaluate( iv_price = 1 iv_quality = 2 iv_delivery = 1 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_1_4 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_e act = ls_result-grade ). + ENDMETHOD. + + METHOD weights_quality. + DATA(ls_result) = mo_cut->evaluate( iv_price = 3 iv_quality = 5 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_3_7 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_b act = ls_result-grade ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/mutation.json b/tasks_gen/eval/G0100/mutation.json new file mode 100644 index 0000000..488f243 --- /dev/null +++ b/tasks_gen/eval/G0100/mutation.json @@ -0,0 +1,79 @@ +{ + "task": "G0100", + "mutants": [ + { + "object": "{{P}}SUPPLIER_SCORE", + "mutant": "line 50: = -> <> (eq)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "GRADE_A_BOUNDARY", + "GRADE_C_BOUNDARY", + "GRADE_D", + "GRADE_E", + "PERFECT_SUPPLIER", + "ROUNDS_UP_TO_B", + "WEIGHTS_QUALITY" + ] + }, + { + "object": "{{P}}SUPPLIER_SCORE", + "mutant": "line 51: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "GRADE_A_BOUNDARY", + "GRADE_C_BOUNDARY", + "GRADE_D", + "GRADE_E", + "PERFECT_SUPPLIER", + "RATING_ABOVE_RANGE", + "ROUNDS_UP_TO_B", + "WEIGHTS_QUALITY" + ] + }, + { + "object": "{{P}}SUPPLIER_SCORE", + "mutant": "line 64: 5 -> 6 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "RATING_ABOVE_RANGE" + ] + }, + { + "object": "{{P}}SUPPLIER_SCORE", + "mutant": "line 69: + -> - (arith)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "GRADE_A_BOUNDARY", + "GRADE_C_BOUNDARY", + "GRADE_D", + "GRADE_E", + "PERFECT_SUPPLIER", + "ROUNDS_UP_TO_B", + "WEIGHTS_QUALITY" + ] + }, + { + "object": "{{P}}SUPPLIER_SCORE", + "mutant": "line 76: >= -> < (rel)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "GRADE_A_BOUNDARY", + "GRADE_C_BOUNDARY", + "GRADE_D", + "GRADE_E", + "PERFECT_SUPPLIER", + "ROUNDS_UP_TO_B", + "WEIGHTS_QUALITY" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0100/reference/supplier_score.clas.abap b/tasks_gen/eval/G0100/reference/supplier_score.clas.abap new file mode 100644 index 0000000..fe25c36 --- /dev/null +++ b/tasks_gen/eval/G0100/reference/supplier_score.clas.abap @@ -0,0 +1,88 @@ +CLASS {{p}}supplier_score DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_rating TYPE i. + TYPES ty_score TYPE p LENGTH 5 DECIMALS 1. + TYPES ty_grade TYPE c LENGTH 1. + TYPES: + BEGIN OF ty_result, + score TYPE ty_score, + grade TYPE ty_grade, + END OF ty_result. + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + CONSTANTS: + c_weight_price TYPE i VALUE 40, + c_weight_quality TYPE i VALUE 35, + c_weight_delivery TYPE i VALUE 25, + c_round_half TYPE i VALUE 5, + c_ten TYPE i VALUE 10, + c_tenth TYPE ty_score VALUE '0.1', + c_threshold_a TYPE ty_score VALUE '4.5', + c_threshold_b TYPE ty_score VALUE '3.5', + c_threshold_c TYPE ty_score VALUE '2.5', + c_threshold_d TYPE ty_score VALUE '1.5', + c_score_blocked TYPE ty_score VALUE '0.0', + c_grade_blocked TYPE ty_grade VALUE 'X'. + + METHODS is_rating_valid + IMPORTING iv_rating TYPE ty_rating + RETURNING VALUE(rv_valid) TYPE abap_bool. + METHODS weighted_score + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rv_score) TYPE ty_score. + METHODS grade_of + IMPORTING iv_score TYPE ty_score + RETURNING VALUE(rv_grade) TYPE ty_grade. +ENDCLASS. + + +CLASS {{p}}supplier_score IMPLEMENTATION. + METHOD evaluate. + IF is_rating_valid( iv_price ) = abap_false + OR is_rating_valid( iv_quality ) = abap_false + OR is_rating_valid( iv_delivery ) = abap_false + OR iv_quality = 1. + rs_result-score = c_score_blocked. + rs_result-grade = c_grade_blocked. + RETURN. + ENDIF. + rs_result-score = weighted_score( iv_price = iv_price + iv_quality = iv_quality + iv_delivery = iv_delivery ). + rs_result-grade = grade_of( rs_result-score ). + ENDMETHOD. + + METHOD is_rating_valid. + rv_valid = xsdbool( iv_rating >= 1 AND iv_rating <= 5 ). + ENDMETHOD. + + METHOD weighted_score. + DATA(lv_points) = iv_price * c_weight_price + + iv_quality * c_weight_quality + + iv_delivery * c_weight_delivery. + DATA(lv_tenths) = ( lv_points + c_round_half ) DIV c_ten. + rv_score = CONV ty_score( lv_tenths ) * c_tenth. + ENDMETHOD. + + METHOD grade_of. + IF iv_score >= c_threshold_a. + rv_grade = 'A'. + ELSEIF iv_score >= c_threshold_b. + rv_grade = 'B'. + ELSEIF iv_score >= c_threshold_c. + rv_grade = 'C'. + ELSEIF iv_score >= c_threshold_d. + rv_grade = 'D'. + ELSE. + rv_grade = 'E'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/reference/supplier_score.testclasses.abap b/tasks_gen/eval/G0100/reference/supplier_score.testclasses.abap new file mode 100644 index 0000000..090837f --- /dev/null +++ b/tasks_gen/eval/G0100/reference/supplier_score.testclasses.abap @@ -0,0 +1,54 @@ +CLASS ltc_supplier_score DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CONSTANTS: + c_score_5_0 TYPE {{p}}supplier_score=>ty_score VALUE '5.0', + c_score_0_0 TYPE {{p}}supplier_score=>ty_score VALUE '0.0', + c_score_3_5 TYPE {{p}}supplier_score=>ty_score VALUE '3.5', + c_grade_a TYPE {{p}}supplier_score=>ty_grade VALUE 'A', + c_grade_b TYPE {{p}}supplier_score=>ty_grade VALUE 'B', + c_grade_x TYPE {{p}}supplier_score=>ty_grade VALUE 'X'. + DATA mo_cut TYPE REF TO {{p}}supplier_score. + METHODS setup. + METHODS perfect_supplier FOR TESTING. + METHODS blocked_quality FOR TESTING. + METHODS invalid_rating FOR TESTING. + METHODS rounded_score FOR TESTING. + METHODS grade_check FOR TESTING. +ENDCLASS. + + +CLASS ltc_supplier_score IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}supplier_score( ). + ENDMETHOD. + + METHOD perfect_supplier. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 5 iv_delivery = 5 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_5_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_a act = ls_result-grade ). + ENDMETHOD. + + METHOD blocked_quality. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 1 iv_delivery = 5 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_0_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_x act = ls_result-grade ). + ENDMETHOD. + + METHOD invalid_rating. + DATA(ls_result) = mo_cut->evaluate( iv_price = 7 iv_quality = 3 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_0_0 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_x act = ls_result-grade ). + ENDMETHOD. + + METHOD rounded_score. + DATA(ls_result) = mo_cut->evaluate( iv_price = 5 iv_quality = 2 iv_delivery = 3 ). + cl_abap_unit_assert=>assert_equals( exp = c_score_3_5 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = c_grade_b act = ls_result-grade ). + ENDMETHOD. + + METHOD grade_check. + DATA(ls_result) = mo_cut->evaluate( iv_price = 1 iv_quality = 2 iv_delivery = 1 ). + cl_abap_unit_assert=>assert_equals( exp = 'E' act = ls_result-grade ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0100/spec.md b/tasks_gen/eval/G0100/spec.md new file mode 100644 index 0000000..1625edd --- /dev/null +++ b/tasks_gen/eval/G0100/spec.md @@ -0,0 +1,62 @@ +# 1. Goal +A purchasing department rates its suppliers. A scorecard program calls the rating +before it prints the scorecard. The program needs the weighted score and the grade +of a supplier. + +# 2. Open questions +None. + +# 3. Context +- All objects are in package $TMP. +- A supplier is rated in three criteria: price, quality, and delivery. +- Each criterion gets an integer rating from 1 (very poor) to 5 (very good). +- The scorecard program passes the three ratings to the class. It reads the score + and the grade from the result. + +# 4. Contract +- Create the class {{P}}SUPPLIER_SCORE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class has these public types: + - TY_RATING: an integer type for one rating. + - TY_SCORE: a packed number with length 5 and 1 decimal place. + - TY_GRADE: a character type with length 1. + - TY_RESULT: a structure with the components SCORE (type TY_SCORE) and GRADE + (type TY_GRADE). +- The class has exactly this public method: + + METHODS evaluate + IMPORTING iv_price TYPE ty_rating + iv_quality TYPE ty_rating + iv_delivery TYPE ty_rating + RETURNING VALUE(rs_result) TYPE ty_result. + +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. + +1. A rating is valid if it is an integer from 1 to 5. If at least one of the three + ratings is not valid, return score 0.0 and grade 'X'. +2. Quality is a knock-out criterion. If the quality rating is 1, return score 0.0 + and grade 'X'. +3. Calculate the weighted score: + score = (price * 40 + quality * 35 + delivery * 25) / 100. +4. Round the score to one decimal place. Use commercial rounding, that is, round + half away from zero. +5. Derive the grade from the rounded score: + - 4.5 or more: 'A' + - 3.5 or more: 'B' + - 2.5 or more: 'C' + - 1.5 or more: 'D' + - less than 1.5: 'E' + +# 6. Constraints +- Release target: SAP_BASIS 816. +- Coding standards: Clean ABAP. Every method has fewer than 40 statements. No + global variables. No comment that restates the code. +- Out of scope: database access, ALV, dynpro, and changes to 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/eval/G0100/task.json b/tasks_gen/eval/G0100/task.json new file mode 100644 index 0000000..6e995fc --- /dev/null +++ b/tasks_gen/eval/G0100/task.json @@ -0,0 +1,41 @@ +{ + "id": "G0100", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 50, + "max_activations": 12 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SUPPLIER_SCORE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the supplier score" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SUPPLIER_SCORE", + "file": "reference/supplier_score.clas.abap", + "description": "Supplier score calculator", + "testclasses_file": "reference/supplier_score.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0101/faulty/m0_park_fee_calculator.clas.abap b/tasks_gen/eval/G0101/faulty/m0_park_fee_calculator.clas.abap new file mode 100644 index 0000000..25b4f86 --- /dev/null +++ b/tasks_gen/eval/G0101/faulty/m0_park_fee_calculator.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_fee. + + PRIVATE SECTION. + CONSTANTS: + c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50', + c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00', + c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00', + c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75', + c_free_minutes TYPE i VALUE 15, + c_max_minutes TYPE i VALUE 43200, + c_seconds_per_day TYPE i VALUE 86400, + c_reference_date TYPE d VALUE '19000101', + c_sunday_offset TYPE i VALUE 6. + + METHODS duration_in_minutes + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + RETURNING VALUE(rv_minutes) TYPE i. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. + METHODS is_sunday + IMPORTING iv_date TYPE d + RETURNING VALUE(rv_sunday) TYPE abap_bool. + METHODS discounted + IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_discount TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}park_fee_calculator IMPLEMENTATION. + METHOD {{p}}if_park_fee~calculate. + DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date + iv_entry_time = iv_entry_time + iv_exit_date = iv_exit_date + iv_exit_time = iv_exit_time ). + IF lv_minutes < 0. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time + fee = 0 ). + RETURN. + ENDIF. + IF lv_minutes <= c_max_minutes. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long + fee = 0 ). + RETURN. + ENDIF. + IF iv_lost_ticket = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = c_lost_ticket ). + RETURN. + ENDIF. + IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = 0 ). + RETURN. + ENDIF. + DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount. + lv_fee = started_hours( lv_minutes ) * c_price_per_hour. + lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ). + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = lv_fee ). + ENDMETHOD. + + METHOD duration_in_minutes. + DATA(lv_entry_time) = iv_entry_time. + DATA(lv_exit_time) = iv_exit_time. + lv_entry_time+4(2) = '00'. + lv_exit_time+4(2) = '00'. + DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day + + ( lv_exit_time - lv_entry_time ). + rv_minutes = lv_seconds DIV 60. + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV 60. + IF iv_minutes MOD 60 <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. + + METHOD is_sunday. + DATA(lv_offset) = iv_date - c_reference_date. + rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ). + ENDMETHOD. + + METHOD discounted. + rv_fee = iv_fee. + IF iv_discount = abap_true. + rv_fee = round( val = iv_fee * c_discount_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/faulty/m1_park_fee_calculator.clas.abap b/tasks_gen/eval/G0101/faulty/m1_park_fee_calculator.clas.abap new file mode 100644 index 0000000..ca97d0b --- /dev/null +++ b/tasks_gen/eval/G0101/faulty/m1_park_fee_calculator.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_fee. + + PRIVATE SECTION. + CONSTANTS: + c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50', + c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00', + c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00', + c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75', + c_free_minutes TYPE i VALUE 15, + c_max_minutes TYPE i VALUE 43200, + c_seconds_per_day TYPE i VALUE 86400, + c_reference_date TYPE d VALUE '19000101', + c_sunday_offset TYPE i VALUE 6. + + METHODS duration_in_minutes + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + RETURNING VALUE(rv_minutes) TYPE i. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. + METHODS is_sunday + IMPORTING iv_date TYPE d + RETURNING VALUE(rv_sunday) TYPE abap_bool. + METHODS discounted + IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_discount TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}park_fee_calculator IMPLEMENTATION. + METHOD {{p}}if_park_fee~calculate. + DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date + iv_entry_time = iv_entry_time + iv_exit_date = iv_exit_date + iv_exit_time = iv_exit_time ). + IF lv_minutes < 0. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time + fee = 0 ). + RETURN. + ENDIF. + IF lv_minutes > c_max_minutes. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long + fee = 0 ). + RETURN. + ENDIF. + IF iv_lost_ticket <> abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = c_lost_ticket ). + RETURN. + ENDIF. + IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = 0 ). + RETURN. + ENDIF. + DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount. + lv_fee = started_hours( lv_minutes ) * c_price_per_hour. + lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ). + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = lv_fee ). + ENDMETHOD. + + METHOD duration_in_minutes. + DATA(lv_entry_time) = iv_entry_time. + DATA(lv_exit_time) = iv_exit_time. + lv_entry_time+4(2) = '00'. + lv_exit_time+4(2) = '00'. + DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day + + ( lv_exit_time - lv_entry_time ). + rv_minutes = lv_seconds DIV 60. + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV 60. + IF iv_minutes MOD 60 <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. + + METHOD is_sunday. + DATA(lv_offset) = iv_date - c_reference_date. + rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ). + ENDMETHOD. + + METHOD discounted. + rv_fee = iv_fee. + IF iv_discount = abap_true. + rv_fee = round( val = iv_fee * c_discount_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/faulty/m2_park_fee_calculator.clas.abap b/tasks_gen/eval/G0101/faulty/m2_park_fee_calculator.clas.abap new file mode 100644 index 0000000..4c02a18 --- /dev/null +++ b/tasks_gen/eval/G0101/faulty/m2_park_fee_calculator.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_fee. + + PRIVATE SECTION. + CONSTANTS: + c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50', + c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00', + c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00', + c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75', + c_free_minutes TYPE i VALUE 15, + c_max_minutes TYPE i VALUE 43200, + c_seconds_per_day TYPE i VALUE 86400, + c_reference_date TYPE d VALUE '19000101', + c_sunday_offset TYPE i VALUE 6. + + METHODS duration_in_minutes + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + RETURNING VALUE(rv_minutes) TYPE i. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. + METHODS is_sunday + IMPORTING iv_date TYPE d + RETURNING VALUE(rv_sunday) TYPE abap_bool. + METHODS discounted + IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_discount TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}park_fee_calculator IMPLEMENTATION. + METHOD {{p}}if_park_fee~calculate. + DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date + iv_entry_time = iv_entry_time + iv_exit_date = iv_exit_date + iv_exit_time = iv_exit_time ). + IF lv_minutes < 0. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time + fee = 0 ). + RETURN. + ENDIF. + IF lv_minutes > c_max_minutes. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long + fee = 0 ). + RETURN. + ENDIF. + IF iv_lost_ticket = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = c_lost_ticket ). + RETURN. + ENDIF. + IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = 0 ). + RETURN. + ENDIF. + DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount. + lv_fee = started_hours( lv_minutes ) / c_price_per_hour. + lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ). + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = lv_fee ). + ENDMETHOD. + + METHOD duration_in_minutes. + DATA(lv_entry_time) = iv_entry_time. + DATA(lv_exit_time) = iv_exit_time. + lv_entry_time+4(2) = '00'. + lv_exit_time+4(2) = '00'. + DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day + + ( lv_exit_time - lv_entry_time ). + rv_minutes = lv_seconds DIV 60. + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV 60. + IF iv_minutes MOD 60 <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. + + METHOD is_sunday. + DATA(lv_offset) = iv_date - c_reference_date. + rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ). + ENDMETHOD. + + METHOD discounted. + rv_fee = iv_fee. + IF iv_discount = abap_true. + rv_fee = round( val = iv_fee * c_discount_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/faulty/m4_park_fee_calculator.clas.abap b/tasks_gen/eval/G0101/faulty/m4_park_fee_calculator.clas.abap new file mode 100644 index 0000000..6336a0f --- /dev/null +++ b/tasks_gen/eval/G0101/faulty/m4_park_fee_calculator.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_fee. + + PRIVATE SECTION. + CONSTANTS: + c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50', + c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00', + c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00', + c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75', + c_free_minutes TYPE i VALUE 15, + c_max_minutes TYPE i VALUE 43200, + c_seconds_per_day TYPE i VALUE 86400, + c_reference_date TYPE d VALUE '19000101', + c_sunday_offset TYPE i VALUE 6. + + METHODS duration_in_minutes + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + RETURNING VALUE(rv_minutes) TYPE i. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. + METHODS is_sunday + IMPORTING iv_date TYPE d + RETURNING VALUE(rv_sunday) TYPE abap_bool. + METHODS discounted + IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_discount TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}park_fee_calculator IMPLEMENTATION. + METHOD {{p}}if_park_fee~calculate. + DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date + iv_entry_time = iv_entry_time + iv_exit_date = iv_exit_date + iv_exit_time = iv_exit_time ). + IF lv_minutes < 0. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time + fee = 0 ). + RETURN. + ENDIF. + IF lv_minutes > c_max_minutes. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long + fee = 0 ). + RETURN. + ENDIF. + IF iv_lost_ticket = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = c_lost_ticket ). + RETURN. + ENDIF. + IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = 0 ). + RETURN. + ENDIF. + DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount. + lv_fee = started_hours( lv_minutes ) * c_price_per_hour. + lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ). + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = lv_fee ). + ENDMETHOD. + + METHOD duration_in_minutes. + DATA(lv_entry_time) = iv_entry_time. + DATA(lv_exit_time) = iv_exit_time. + lv_entry_time+4(2) = '00'. + lv_exit_time+4(2) = '00'. + DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day + + ( lv_exit_time - lv_entry_time ). + rv_minutes = lv_seconds DIV 60. + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV 60. + IF iv_minutes MOD 60 <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. + + METHOD is_sunday. + DATA(lv_offset) = iv_date - c_reference_date. + rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ). + ENDMETHOD. + + METHOD discounted. + rv_fee = iv_fee. + IF iv_discount = abap_false. + rv_fee = round( val = iv_fee * c_discount_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/generation.json b/tasks_gen/eval/G0101/generation.json new file mode 100644 index 0000000..c24e778 --- /dev/null +++ b/tasks_gen/eval/G0101/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0101", + "pool": "eval", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.2, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.2, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0101/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0101/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..af5a7e7 --- /dev/null +++ b/tasks_gen/eval/G0101/hidden/t02_hidden.clas.abap @@ -0,0 +1,156 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_park_fee. + + METHODS setup. + METHODS assert_result + IMPORTING is_result TYPE {{p}}if_park_fee=>ty_result + iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_reason TYPE {{p}}if_park_fee=>ty_reason. + + METHODS bad_time_order FOR TESTING. + METHODS too_long FOR TESTING. + METHODS free_period FOR TESTING. + METHODS one_started_hour FOR TESTING. + METHODS two_started_hours FOR TESTING. + METHODS sunday_free FOR TESTING. + METHODS lost_ticket_flat FOR TESTING. + METHODS lost_ticket_on_sunday FOR TESTING. + METHODS discount_rounds FOR TESTING. + METHODS discount_rounds_up FOR TESTING. + METHODS daily_cap FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}park_fee_calculator( ). + ENDMETHOD. + + METHOD assert_result. + cl_abap_unit_assert=>assert_equals( exp = iv_fee act = is_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_result-reason ). + ENDMETHOD. + + METHOD bad_time_order. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '120000' + iv_exit_date = '20240108' + iv_exit_time = '110000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-bad_time ). + ENDMETHOD. + + METHOD too_long. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240208' + iv_exit_time = '100100' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-too_long ). + ENDMETHOD. + + METHOD free_period. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '100015' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD one_started_hour. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '103000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '2.50' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD two_started_hours. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '110100' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '5.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD sunday_free. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107' + iv_entry_time = '100000' + iv_exit_date = '20240107' + iv_exit_time = '130000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD lost_ticket_flat. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '100500' + iv_lost_ticket = abap_true + iv_discount = abap_false ) + iv_fee = '45.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD lost_ticket_on_sunday. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107' + iv_entry_time = '100000' + iv_exit_date = '20240107' + iv_exit_time = '120000' + iv_lost_ticket = abap_true + iv_discount = abap_false ) + iv_fee = '45.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD discount_rounds. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '110100' + iv_lost_ticket = abap_false + iv_discount = abap_true ) + iv_fee = '3.75' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD discount_rounds_up. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '103000' + iv_lost_ticket = abap_false + iv_discount = abap_true ) + iv_fee = '1.88' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD daily_cap. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '060000' + iv_exit_date = '20240109' + iv_exit_time = '120000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '60.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/mutation.json b/tasks_gen/eval/G0101/mutation.json new file mode 100644 index 0000000..7ca9691 --- /dev/null +++ b/tasks_gen/eval/G0101/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G0101", + "mutants": [ + { + "object": "{{P}}PARK_FEE_CALCULATOR", + "mutant": "line 47: > -> <= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "DAILY_CAP", + "DISCOUNT_ROUNDS", + "DISCOUNT_ROUNDS_UP", + "FREE_PERIOD", + "LOST_TICKET_FLAT", + "LOST_TICKET_ON_SUNDAY", + "ONE_STARTED_HOUR", + "SUNDAY_FREE", + "TOO_LONG", + "TWO_STARTED_HOURS" + ] + }, + { + "object": "{{P}}PARK_FEE_CALCULATOR", + "mutant": "line 52: = -> <> (eq)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "DAILY_CAP", + "DISCOUNT_ROUNDS", + "DISCOUNT_ROUNDS_UP", + "FREE_PERIOD", + "LOST_TICKET_FLAT", + "LOST_TICKET_ON_SUNDAY", + "ONE_STARTED_HOUR", + "SUNDAY_FREE", + "TWO_STARTED_HOURS" + ] + }, + { + "object": "{{P}}PARK_FEE_CALCULATOR", + "mutant": "line 63: * -> / (arith)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "DAILY_CAP", + "DISCOUNT_ROUNDS", + "DISCOUNT_ROUNDS_UP", + "ONE_STARTED_HOUR", + "TWO_STARTED_HOURS" + ] + }, + { + "object": "{{P}}PARK_FEE_CALCULATOR", + "mutant": "line 76: 2 -> 3 (const)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}PARK_FEE_CALCULATOR", + "mutant": "line 96: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "DAILY_CAP", + "DISCOUNT_ROUNDS", + "DISCOUNT_ROUNDS_UP", + "ONE_STARTED_HOUR", + "TWO_STARTED_HOURS" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0101/reference/park_fee_calculator.clas.abap b/tasks_gen/eval/G0101/reference/park_fee_calculator.clas.abap new file mode 100644 index 0000000..68822c4 --- /dev/null +++ b/tasks_gen/eval/G0101/reference/park_fee_calculator.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_park_fee. + + PRIVATE SECTION. + CONSTANTS: + c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50', + c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00', + c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00', + c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75', + c_free_minutes TYPE i VALUE 15, + c_max_minutes TYPE i VALUE 43200, + c_seconds_per_day TYPE i VALUE 86400, + c_reference_date TYPE d VALUE '19000101', + c_sunday_offset TYPE i VALUE 6. + + METHODS duration_in_minutes + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + RETURNING VALUE(rv_minutes) TYPE i. + METHODS started_hours + IMPORTING iv_minutes TYPE i + RETURNING VALUE(rv_hours) TYPE i. + METHODS is_sunday + IMPORTING iv_date TYPE d + RETURNING VALUE(rv_sunday) TYPE abap_bool. + METHODS discounted + IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_discount TYPE abap_bool + RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount. +ENDCLASS. + + +CLASS {{p}}park_fee_calculator IMPLEMENTATION. + METHOD {{p}}if_park_fee~calculate. + DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date + iv_entry_time = iv_entry_time + iv_exit_date = iv_exit_date + iv_exit_time = iv_exit_time ). + IF lv_minutes < 0. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time + fee = 0 ). + RETURN. + ENDIF. + IF lv_minutes > c_max_minutes. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long + fee = 0 ). + RETURN. + ENDIF. + IF iv_lost_ticket = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = c_lost_ticket ). + RETURN. + ENDIF. + IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = 0 ). + RETURN. + ENDIF. + DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount. + lv_fee = started_hours( lv_minutes ) * c_price_per_hour. + lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ). + IF lv_fee > c_max_fee. + lv_fee = c_max_fee. + ENDIF. + rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok + fee = lv_fee ). + ENDMETHOD. + + METHOD duration_in_minutes. + DATA(lv_entry_time) = iv_entry_time. + DATA(lv_exit_time) = iv_exit_time. + lv_entry_time+4(2) = '00'. + lv_exit_time+4(2) = '00'. + DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day + + ( lv_exit_time - lv_entry_time ). + rv_minutes = lv_seconds DIV 60. + ENDMETHOD. + + METHOD started_hours. + rv_hours = iv_minutes DIV 60. + IF iv_minutes MOD 60 <> 0. + rv_hours = rv_hours + 1. + ENDIF. + ENDMETHOD. + + METHOD is_sunday. + DATA(lv_offset) = iv_date - c_reference_date. + rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ). + ENDMETHOD. + + METHOD discounted. + rv_fee = iv_fee. + IF iv_discount = abap_true. + rv_fee = round( val = iv_fee * c_discount_rate + dec = 2 + mode = cl_abap_math=>round_half_up ). + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/reference/park_fee_calculator.testclasses.abap b/tasks_gen/eval/G0101/reference/park_fee_calculator.testclasses.abap new file mode 100644 index 0000000..8e36f01 --- /dev/null +++ b/tasks_gen/eval/G0101/reference/park_fee_calculator.testclasses.abap @@ -0,0 +1,143 @@ +CLASS ltc_park_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_park_fee. + + METHODS setup. + METHODS assert_result + IMPORTING is_result TYPE {{p}}if_park_fee=>ty_result + iv_fee TYPE {{p}}if_park_fee=>ty_amount + iv_reason TYPE {{p}}if_park_fee=>ty_reason. + + METHODS free_period_is_free FOR TESTING. + METHODS one_hour_costs_250 FOR TESTING. + METHODS two_hours_cost_500 FOR TESTING. + METHODS sunday_is_free FOR TESTING. + METHODS lost_ticket_is_flat FOR TESTING. + METHODS discount_is_rounded FOR TESTING. + METHODS discount_rounds_up FOR TESTING. + METHODS fee_is_capped FOR TESTING. + METHODS exit_before_entry FOR TESTING. + METHODS long_parking FOR TESTING. +ENDCLASS. + + +CLASS ltc_park_fee IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}park_fee_calculator( ). + ENDMETHOD. + + METHOD assert_result. + cl_abap_unit_assert=>assert_equals( exp = iv_fee act = is_result-fee ). + cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_result-reason ). + ENDMETHOD. + + METHOD free_period_is_free. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '100015' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD one_hour_costs_250. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '103000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '2.50' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD two_hours_cost_500. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '110100' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '5.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD sunday_is_free. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107' + iv_entry_time = '090000' + iv_exit_date = '20240107' + iv_exit_time = '120000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD lost_ticket_is_flat. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '100500' + iv_lost_ticket = abap_true + iv_discount = abap_false ) + iv_fee = '45.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD discount_is_rounded. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '110100' + iv_lost_ticket = abap_false + iv_discount = abap_true ) + iv_fee = '3.75' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD discount_rounds_up. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240108' + iv_exit_time = '103000' + iv_lost_ticket = abap_false + iv_discount = abap_true ) + iv_fee = '1.88' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD fee_is_capped. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '060000' + iv_exit_date = '20240109' + iv_exit_time = '120000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '60.00' + iv_reason = {{p}}if_park_fee=>c_reason-ok ). + ENDMETHOD. + + METHOD exit_before_entry. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '120000' + iv_exit_date = '20240108' + iv_exit_time = '110000' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-bad_time ). + ENDMETHOD. + + METHOD long_parking. + assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108' + iv_entry_time = '100000' + iv_exit_date = '20240208' + iv_exit_time = '100100' + iv_lost_ticket = abap_false + iv_discount = abap_false ) + iv_fee = '0.00' + iv_reason = {{p}}if_park_fee=>c_reason-too_long ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0101/seed/if_park_fee.intf.abap b/tasks_gen/eval/G0101/seed/if_park_fee.intf.abap new file mode 100644 index 0000000..7137a98 --- /dev/null +++ b/tasks_gen/eval/G0101/seed/if_park_fee.intf.abap @@ -0,0 +1,23 @@ +INTERFACE {{p}}if_park_fee PUBLIC. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES ty_reason TYPE c LENGTH 10. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + fee TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + bad_time TYPE ty_reason VALUE 'BAD_TIME', + too_long TYPE ty_reason VALUE 'TOO_LONG', + END OF c_reason. + METHODS calculate + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + iv_lost_ticket TYPE abap_bool + iv_discount TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/eval/G0101/spec.md b/tasks_gen/eval/G0101/spec.md new file mode 100644 index 0000000..fd0bbaa --- /dev/null +++ b/tasks_gen/eval/G0101/spec.md @@ -0,0 +1,67 @@ +# 1. Goal +A car park operator calculates the parking fee of a car. The calculation uses +the entry time and the exit time of the car. A payment terminal calls the +calculation before it charges the driver. The terminal needs the fee and a +reason code for each parking event. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_PARK_FEE exists in package $TMP. It is active. +- The interface contains the type TY_AMOUNT (a packed number, length 8, 2 +decimals), the type TY_REASON (a character field, length 10), the structure +TY_RESULT with the components REASON (TY_REASON) and FEE (TY_AMOUNT), and the +constant structure C_REASON with the components OK, BAD_TIME and TOO_LONG. +- The interface contains one method: + CALCULATE + IMPORTING iv_entry_date TYPE d + iv_entry_time TYPE t + iv_exit_date TYPE d + iv_exit_time TYPE t + iv_lost_ticket TYPE abap_bool + iv_discount TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}PARK_FEE_CALCULATOR in package $TMP. +- The class is public and final. It has a public constructor without + parameters. +- The class implements the interface {{P}}IF_PARK_FEE. +- Do not add other public methods. + +# 5. Business rules +The method CALCULATE applies the rules in this sequence. It returns the reason +and the fee of the first rule that applies. +1. The duration is the number of whole minutes between the entry and the exit. + Ignore the seconds of the two time fields. Example: the entry 10:00:30 and + the exit 10:01:20 give the duration 1 minute. The entry 10:00:00 and the + exit 10:30:00 give the duration 30 minutes. +2. If the exit is before the entry, return the reason BAD_TIME and the fee + 0.00. +3. If the duration is more than 43200 minutes, return the reason TOO_LONG and + the fee 0.00. +4. If the ticket is lost (iv_lost_ticket = abap_true), the fee is 45.00 and the + reason is OK. Rules 5 to 9 do not apply. +5. If the duration is 15 minutes or less, the fee is 0.00 and the reason is OK. +6. If the entry date is a Sunday, the fee is 0.00 and the reason is OK. +7. Else the fee is the number of started hours multiplied by 2.50. A started + hour is a full hour or a part of an hour. Example: 61 minutes are 2 started + hours and give the fee 5.00. 30 minutes are 1 started hour and give the fee + 2.50. +8. If the driver uses the discount card (iv_discount = abap_true), reduce the + fee by 25 percent. Round the result to 2 decimals, half up. Example: the fee + 5.00 becomes 3.75. The fee 2.50 becomes 1.88. +9. The fee never exceeds 60.00. If the fee is higher, it is 60.00. +10. The reason is OK. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Keep every method below 40 statements. Do not + use global variables. Do not write comments that restate the code. +- Out of scope: do not change the interface {{P}}IF_PARK_FEE. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/eval/G0101/task.json b/tasks_gen/eval/G0101/task.json new file mode 100644 index 0000000..8019629 --- /dev/null +++ b/tasks_gen/eval/G0101/task.json @@ -0,0 +1,51 @@ +{ + "id": "G0101", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_PARK_FEE", + "file": "seed/if_park_fee.intf.abap", + "description": "Parking fee contract" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}PARK_FEE_CALCULATOR", + "implements": "{{P}}IF_PARK_FEE" + } + ], + "out_of_scope": [ + "{{P}}IF_PARK_FEE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}PARK_FEE_CALCULATOR", + "file": "reference/park_fee_calculator.clas.abap", + "description": "Parking fee calculator", + "testclasses_file": "reference/park_fee_calculator.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0102/faulty/m0_hotel_rate.clas.abap b/tasks_gen/eval/G0102/faulty/m0_hotel_rate.clas.abap new file mode 100644 index 0000000..6f006db --- /dev/null +++ b/tasks_gen/eval/G0102/faulty/m0_hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category = {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + AND iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 1 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total - lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_true. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/faulty/m1_hotel_rate.clas.abap b/tasks_gen/eval/G0102/faulty/m1_hotel_rate.clas.abap new file mode 100644 index 0000000..09f4b38 --- /dev/null +++ b/tasks_gen/eval/G0102/faulty/m1_hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category <> {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + OR iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 1 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total - lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_true. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/faulty/m2_hotel_rate.clas.abap b/tasks_gen/eval/G0102/faulty/m2_hotel_rate.clas.abap new file mode 100644 index 0000000..50c197e --- /dev/null +++ b/tasks_gen/eval/G0102/faulty/m2_hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category <> {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + AND iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 2 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total - lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_true. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/faulty/m3_hotel_rate.clas.abap b/tasks_gen/eval/G0102/faulty/m3_hotel_rate.clas.abap new file mode 100644 index 0000000..38bca52 --- /dev/null +++ b/tasks_gen/eval/G0102/faulty/m3_hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category <> {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + AND iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 1 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total + lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_true. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/faulty/m4_hotel_rate.clas.abap b/tasks_gen/eval/G0102/faulty/m4_hotel_rate.clas.abap new file mode 100644 index 0000000..141aec2 --- /dev/null +++ b/tasks_gen/eval/G0102/faulty/m4_hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category <> {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + AND iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 1 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total - lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_false. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/generation.json b/tasks_gen/eval/G0102/generation.json new file mode 100644 index 0000000..e6a85d8 --- /dev/null +++ b/tasks_gen/eval/G0102/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0102", + "pool": "eval", + "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/eval/G0102/hidden/t13_hidden.clas.abap b/tasks_gen/eval/G0102/hidden/t13_hidden.clas.abap new file mode 100644 index 0000000..ab04783 --- /dev/null +++ b/tasks_gen/eval/G0102/hidden/t13_hidden.clas.abap @@ -0,0 +1,145 @@ +CLASS {{p}}t13_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_hotel_rate. + METHODS setup. + METHODS expect_price + IMPORTING iv_expected TYPE {{p}}if_hotel_rate=>ty_amount + iv_actual TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS rejects_zero_nights FOR TESTING. + METHODS rejects_zero_guests FOR TESTING. + METHODS rejects_unknown_category FOR TESTING. + METHODS validation_order FOR TESTING. + METHODS single_standard_night FOR TESTING. + METHODS two_guests_one_room FOR TESTING. + METHODS three_guests_two_rooms FOR TESTING. + METHODS breakfast_per_guest FOR TESTING. + METHODS discount_starts_at_seven FOR TESTING. + METHODS discount_fifteen_percent FOR TESTING. + METHODS discount_twenty_percent FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t13_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}hotel_rate( ). + ENDMETHOD. + + METHOD expect_price. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD rejects_zero_nights. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 0 + iv_guests = 2 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-nights + act = ls_result-reason ). + expect_price( iv_expected = '0.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD rejects_zero_guests. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 3 + iv_guests = 0 + iv_breakfast = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-guests + act = ls_result-reason ). + expect_price( iv_expected = '0.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD rejects_unknown_category. + DATA(ls_result) = mo_cut->calculate( iv_category = 'X' + iv_nights = 2 + iv_guests = 1 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-category + act = ls_result-reason ). + expect_price( iv_expected = '0.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD validation_order. + DATA(ls_result) = mo_cut->calculate( iv_category = 'X' + iv_nights = 0 + iv_guests = 0 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-nights + act = ls_result-reason ). + ls_result = mo_cut->calculate( iv_category = 'X' + iv_nights = 2 + iv_guests = 0 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-guests + act = ls_result-reason ). + ls_result = mo_cut->calculate( iv_category = 'X' + iv_nights = 2 + iv_guests = 2 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-category + act = ls_result-reason ). + ENDMETHOD. + + METHOD single_standard_night. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 1 + iv_guests = 1 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-ok + act = ls_result-reason ). + expect_price( iv_expected = '93.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD two_guests_one_room. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-comfort + iv_nights = 3 + iv_guests = 2 + iv_breakfast = abap_false ). + expect_price( iv_expected = '438.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD three_guests_two_rooms. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-comfort + iv_nights = 2 + iv_guests = 3 + iv_breakfast = abap_false ). + expect_price( iv_expected = '578.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD breakfast_per_guest. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 2 + iv_guests = 2 + iv_breakfast = abap_true ). + expect_price( iv_expected = '242.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD discount_starts_at_seven. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 6 + iv_guests = 1 + iv_breakfast = abap_false ). + expect_price( iv_expected = '558.00' iv_actual = ls_result-price ). + ls_result = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 7 + iv_guests = 1 + iv_breakfast = abap_false ). + expect_price( iv_expected = '588.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD discount_fifteen_percent. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-comfort + iv_nights = 14 + iv_guests = 2 + iv_breakfast = abap_false ). + expect_price( iv_expected = '1750.00' iv_actual = ls_result-price ). + ENDMETHOD. + + METHOD discount_twenty_percent. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-suite + iv_nights = 28 + iv_guests = 1 + iv_breakfast = abap_false ). + expect_price( iv_expected = '5908.00' iv_actual = ls_result-price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/mutation.json b/tasks_gen/eval/G0102/mutation.json new file mode 100644 index 0000000..6624423 --- /dev/null +++ b/tasks_gen/eval/G0102/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G0102", + "mutants": [ + { + "object": "{{P}}HOTEL_RATE", + "mutant": "line 59: <> -> = (rel)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "BREAKFAST_PER_GUEST", + "DISCOUNT_STARTS_AT_SEVEN", + "REJECTS_UNKNOWN_CATEGORY", + "SINGLE_STANDARD_NIGHT", + "VALIDATION_ORDER" + ] + }, + { + "object": "{{P}}HOTEL_RATE", + "mutant": "line 61: AND -> OR (logic)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "BREAKFAST_PER_GUEST", + "DISCOUNT_FIFTEEN_PERCENT", + "DISCOUNT_STARTS_AT_SEVEN", + "DISCOUNT_TWENTY_PERCENT", + "SINGLE_STANDARD_NIGHT", + "THREE_GUESTS_TWO_ROOMS", + "TWO_GUESTS_ONE_ROOM" + ] + }, + { + "object": "{{P}}HOTEL_RATE", + "mutant": "line 69: 1 -> 2 (const)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "BREAKFAST_PER_GUEST", + "DISCOUNT_FIFTEEN_PERCENT", + "TWO_GUESTS_ONE_ROOM" + ] + }, + { + "object": "{{P}}HOTEL_RATE", + "mutant": "line 98: - -> + (arith)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "DISCOUNT_FIFTEEN_PERCENT", + "DISCOUNT_STARTS_AT_SEVEN", + "DISCOUNT_TWENTY_PERCENT" + ] + }, + { + "object": "{{P}}HOTEL_RATE", + "mutant": "line 104: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "BREAKFAST_PER_GUEST", + "DISCOUNT_FIFTEEN_PERCENT", + "DISCOUNT_STARTS_AT_SEVEN", + "DISCOUNT_TWENTY_PERCENT", + "SINGLE_STANDARD_NIGHT", + "THREE_GUESTS_TWO_ROOMS", + "TWO_GUESTS_ONE_ROOM" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0102/reference/hotel_rate.clas.abap b/tasks_gen/eval/G0102/reference/hotel_rate.clas.abap new file mode 100644 index 0000000..a407ee3 --- /dev/null +++ b/tasks_gen/eval/G0102/reference/hotel_rate.clas.abap @@ -0,0 +1,108 @@ +CLASS {{p}}hotel_rate DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_hotel_rate. + + PRIVATE SECTION. + CONSTANTS: + c_breakfast TYPE {{p}}if_hotel_rate=>ty_amount VALUE '12.50', + c_city_tax TYPE {{p}}if_hotel_rate=>ty_amount VALUE '3.00'. + + METHODS validate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_reason) TYPE {{p}}if_hotel_rate=>ty_reason. + METHODS room_count + IMPORTING iv_guests TYPE i + RETURNING VALUE(rv_rooms) TYPE i. + METHODS nightly_rate + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + RETURNING VALUE(rv_amount) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS discount_percent + IMPORTING iv_nights TYPE i + RETURNING VALUE(rv_percent) TYPE i. + METHODS net_room_total + IMPORTING iv_category TYPE {{p}}if_hotel_rate=>ty_category + iv_nights TYPE i + iv_guests TYPE i + RETURNING VALUE(rv_total) TYPE {{p}}if_hotel_rate=>ty_amount. + METHODS extras + IMPORTING iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rv_extras) TYPE {{p}}if_hotel_rate=>ty_amount. +ENDCLASS. + + +CLASS {{p}}hotel_rate IMPLEMENTATION. + METHOD {{p}}if_hotel_rate~calculate. + rs_result-reason = validate( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ). + IF rs_result-reason <> {{p}}if_hotel_rate=>c_reason-ok. + rs_result-price = 0. + RETURN. + ENDIF. + rs_result-price = net_room_total( iv_category = iv_category + iv_nights = iv_nights + iv_guests = iv_guests ) + + extras( iv_nights = iv_nights + iv_guests = iv_guests + iv_breakfast = iv_breakfast ). + ENDMETHOD. + + METHOD validate. + IF iv_nights < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-nights. + ELSEIF iv_guests < 1. + rv_reason = {{p}}if_hotel_rate=>c_reason-guests. + ELSEIF iv_category <> {{p}}if_hotel_rate=>c_category-basic + AND iv_category <> {{p}}if_hotel_rate=>c_category-comfort + AND iv_category <> {{p}}if_hotel_rate=>c_category-suite. + rv_reason = {{p}}if_hotel_rate=>c_reason-category. + ELSE. + rv_reason = {{p}}if_hotel_rate=>c_reason-ok. + ENDIF. + ENDMETHOD. + + METHOD room_count. + rv_rooms = ( iv_guests + 1 ) DIV 2. + ENDMETHOD. + + METHOD nightly_rate. + CASE iv_category. + WHEN {{p}}if_hotel_rate=>c_category-basic. + rv_amount = '90.00'. + WHEN {{p}}if_hotel_rate=>c_category-comfort. + rv_amount = '140.00'. + WHEN {{p}}if_hotel_rate=>c_category-suite. + rv_amount = '260.00'. + ENDCASE. + ENDMETHOD. + + METHOD discount_percent. + IF iv_nights >= 28. + rv_percent = 20. + ELSEIF iv_nights >= 14. + rv_percent = 15. + ELSEIF iv_nights >= 7. + rv_percent = 10. + ENDIF. + ENDMETHOD. + + METHOD net_room_total. + DATA lv_total TYPE {{p}}if_hotel_rate=>ty_amount. + lv_total = nightly_rate( iv_category ) * room_count( iv_guests ) * iv_nights. + DATA lv_discount TYPE {{p}}if_hotel_rate=>ty_amount. + lv_discount = lv_total * discount_percent( iv_nights ) / 100. + rv_total = lv_total - lv_discount. + ENDMETHOD. + + METHOD extras. + DATA(lv_guest_nights) = iv_guests * iv_nights. + rv_extras = lv_guest_nights * c_city_tax. + IF iv_breakfast = abap_true. + rv_extras = rv_extras + lv_guest_nights * c_breakfast. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/reference/hotel_rate.testclasses.abap b/tasks_gen/eval/G0102/reference/hotel_rate.testclasses.abap new file mode 100644 index 0000000..ac4d83a --- /dev/null +++ b/tasks_gen/eval/G0102/reference/hotel_rate.testclasses.abap @@ -0,0 +1,76 @@ +CLASS ltc_hotel_rate DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_hotel_rate. + METHODS setup. + METHODS basic_room FOR TESTING. + METHODS invalid_nights FOR TESTING. + METHODS invalid_guests FOR TESTING. + METHODS invalid_category FOR TESTING. + METHODS breakfast_and_tax FOR TESTING. + METHODS long_stay_discount FOR TESTING. +ENDCLASS. + + +CLASS ltc_hotel_rate IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}hotel_rate( ). + ENDMETHOD. + + METHOD basic_room. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 1 + iv_guests = 1 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-ok + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}if_hotel_rate=>ty_amount( '93.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_nights. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-comfort + iv_nights = 0 + iv_guests = 2 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-nights + act = ls_result-reason ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}if_hotel_rate=>ty_amount( '0.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD invalid_guests. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 4 + iv_guests = 0 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-guests + act = ls_result-reason ). + ENDMETHOD. + + METHOD invalid_category. + DATA(ls_result) = mo_cut->calculate( iv_category = 'Z' + iv_nights = 4 + iv_guests = 2 + iv_breakfast = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = {{p}}if_hotel_rate=>c_reason-category + act = ls_result-reason ). + ENDMETHOD. + + METHOD breakfast_and_tax. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-basic + iv_nights = 2 + iv_guests = 2 + iv_breakfast = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}if_hotel_rate=>ty_amount( '242.00' ) + act = ls_result-price ). + ENDMETHOD. + + METHOD long_stay_discount. + DATA(ls_result) = mo_cut->calculate( iv_category = {{p}}if_hotel_rate=>c_category-comfort + iv_nights = 14 + iv_guests = 2 + iv_breakfast = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}if_hotel_rate=>ty_amount( '1750.00' ) + act = ls_result-price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0102/seed/if_hotel_rate.intf.abap b/tasks_gen/eval/G0102/seed/if_hotel_rate.intf.abap new file mode 100644 index 0000000..ab8d428 --- /dev/null +++ b/tasks_gen/eval/G0102/seed/if_hotel_rate.intf.abap @@ -0,0 +1,29 @@ +INTERFACE {{p}}if_hotel_rate PUBLIC. + TYPES ty_category TYPE c LENGTH 1. + TYPES ty_reason TYPE c LENGTH 10. + TYPES ty_amount TYPE p LENGTH 10 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + reason TYPE ty_reason, + price TYPE ty_amount, + END OF ty_result. + CONSTANTS: + BEGIN OF c_reason, + ok TYPE ty_reason VALUE 'OK', + nights TYPE ty_reason VALUE 'NIGHTS', + guests TYPE ty_reason VALUE 'GUESTS', + category TYPE ty_reason VALUE 'CATEGORY', + END OF c_reason. + CONSTANTS: + BEGIN OF c_category, + basic TYPE ty_category VALUE 'S', + comfort TYPE ty_category VALUE 'C', + suite TYPE ty_category VALUE 'U', + END OF c_category. + METHODS calculate + IMPORTING iv_category TYPE ty_category + iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. +ENDINTERFACE. diff --git a/tasks_gen/eval/G0102/spec.md b/tasks_gen/eval/G0102/spec.md new file mode 100644 index 0000000..c722987 --- /dev/null +++ b/tasks_gen/eval/G0102/spec.md @@ -0,0 +1,71 @@ +# 1. Goal +Calculate the price of a hotel stay. A booking program calls the calculation +before it shows the price to the guest. The program needs one reason code for +each invalid input. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_HOTEL_RATE exists in package $TMP. It is active. +- The interface has these types: + - TY_CATEGORY: character, length 1. + - TY_REASON: character, length 10. + - TY_AMOUNT: packed number, length 10, 2 decimals. + - TY_RESULT: structure with the components REASON (TY_REASON) and + PRICE (TY_AMOUNT). +- The interface has these constants: + - C_REASON: OK, NIGHTS, GUESTS, CATEGORY. + - C_CATEGORY: BASIC 'S', COMFORT 'C', SUITE 'U'. +- The interface has one method: + CALCULATE + IMPORTING iv_category TYPE ty_category + iv_nights TYPE i + iv_guests TYPE i + iv_breakfast TYPE abap_bool + RETURNING VALUE(rs_result) TYPE ty_result. + +# 4. Contract +- Create the class {{P}}HOTEL_RATE in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_HOTEL_RATE. +- Do not add other public methods. + +# 5. Business rules +Check the input in this sequence. Return the reason of the first check that +fails and the price 0. +1. If IV_NIGHTS is less than 1, return NIGHTS. +2. If IV_GUESTS is less than 1, return GUESTS. +3. If IV_CATEGORY is not BASIC, COMFORT or SUITE, return CATEGORY. +4. Else return OK and calculate the price. + +Calculate the price in this sequence: +1. One room takes at most 2 guests. The number of rooms is the number of guests +divided by 2, rounded up. +2. The price of one room for one night is 90.00 for BASIC, 140.00 for COMFORT +and 260.00 for SUITE. +3. The room total is the number of rooms multiplied by the number of nights +multiplied by the price of one room for one night. +4. Long stay discount on the room total: + - 28 nights or more: 20 % + - 14 nights or more: 15 % + - 7 nights or more: 10 % + - less than 7 nights: no discount + The discounted room total is the room total minus the discount. +5. Breakfast costs 12.50 for one guest for one night. Add it if IV_BREAKFAST is +true. +6. City tax costs 3.00 for one guest for one night. Add it always. +7. The price is the discounted room total plus breakfast plus city tax. + +All amounts are exact to two decimals. + +# 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_HOTEL_RATE. + +# 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/eval/G0102/task.json b/tasks_gen/eval/G0102/task.json new file mode 100644 index 0000000..ea2a7b5 --- /dev/null +++ b/tasks_gen/eval/G0102/task.json @@ -0,0 +1,51 @@ +{ + "id": "G0102", + "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_HOTEL_RATE", + "file": "seed/if_hotel_rate.intf.abap", + "description": "Hotel rate contract: types, reason codes, category codes and the calculate signature" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}HOTEL_RATE", + "implements": "{{P}}IF_HOTEL_RATE" + } + ], + "out_of_scope": [ + "{{P}}IF_HOTEL_RATE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T13_HIDDEN", + "file": "hidden/t13_hidden.clas.abap", + "description": "T13 hidden tests for the hotel rate calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}HOTEL_RATE", + "file": "reference/hotel_rate.clas.abap", + "description": "Hotel rate calculator", + "testclasses_file": "reference/hotel_rate.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0103/faulty/m0_overdue_fine.clas.abap b/tasks_gen/eval/G0103/faulty/m0_overdue_fine.clas.abap new file mode 100644 index 0000000..ab97b0e --- /dev/null +++ b/tasks_gen/eval/G0103/faulty/m0_overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue > 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue - ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days * ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine + c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 7 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/faulty/m1_overdue_fine.clas.abap b/tasks_gen/eval/G0103/faulty/m1_overdue_fine.clas.abap new file mode 100644 index 0000000..708b73a --- /dev/null +++ b/tasks_gen/eval/G0103/faulty/m1_overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue <= 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue + ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days * ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine + c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 7 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/faulty/m2_overdue_fine.clas.abap b/tasks_gen/eval/G0103/faulty/m2_overdue_fine.clas.abap new file mode 100644 index 0000000..e304bcc --- /dev/null +++ b/tasks_gen/eval/G0103/faulty/m2_overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue <= 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue - ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days / ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine + c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 7 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/faulty/m3_overdue_fine.clas.abap b/tasks_gen/eval/G0103/faulty/m3_overdue_fine.clas.abap new file mode 100644 index 0000000..f5fbc5f --- /dev/null +++ b/tasks_gen/eval/G0103/faulty/m3_overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue <= 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue - ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days * ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine - c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 7 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/faulty/m4_overdue_fine.clas.abap b/tasks_gen/eval/G0103/faulty/m4_overdue_fine.clas.abap new file mode 100644 index 0000000..0c4af2e --- /dev/null +++ b/tasks_gen/eval/G0103/faulty/m4_overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue <= 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue - ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days * ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine + c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 8 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/generation.json b/tasks_gen/eval/G0103/generation.json new file mode 100644 index 0000000..dfc5057 --- /dev/null +++ b/tasks_gen/eval/G0103/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0103", + "pool": "eval", + "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/eval/G0103/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0103/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..3b085ae --- /dev/null +++ b/tasks_gen/eval/G0103/hidden/t02_hidden.clas.abap @@ -0,0 +1,103 @@ +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}}overdue_fine. + METHODS setup. + METHODS assert_fine + IMPORTING iv_due TYPE d + iv_return TYPE d + iv_media TYPE {{p}}overdue_fine=>ty_media + iv_days TYPE i + iv_charge TYPE i + iv_fine TYPE {{p}}overdue_fine=>ty_amount. + METHODS not_late_no_fine FOR TESTING. + METHODS book_in_grace FOR TESTING. + METHODS book_over_grace FOR TESTING. + METHODS dvd_in_grace FOR TESTING. + METHODS dvd_over_grace FOR TESTING. + METHODS game_no_grace FOR TESTING. + METHODS reminder_fee_added FOR TESTING. + METHODS no_reminder_day_30 FOR TESTING. + METHODS fine_capped FOR TESTING. + METHODS cap_after_reminder FOR TESTING. + METHODS unknown_media_no_fine FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}overdue_fine( ). + ENDMETHOD. + + METHOD assert_fine. + DATA(ls_result) = mo_cut->calculate( iv_due_date = iv_due + iv_return_date = iv_return + iv_media = iv_media ). + cl_abap_unit_assert=>assert_equals( exp = iv_days + act = ls_result-days_overdue + msg = |days overdue for { iv_media }| ). + cl_abap_unit_assert=>assert_equals( exp = iv_charge + act = ls_result-chargeable_days + msg = |chargeable days for { iv_media }| ). + cl_abap_unit_assert=>assert_equals( exp = iv_fine + act = ls_result-fine + msg = |fine for { iv_media }| ). + ENDMETHOD. + + METHOD not_late_no_fine. + assert_fine( iv_due = '20250110' iv_return = '20250109' iv_media = 'BOOK' + iv_days = 0 iv_charge = 0 iv_fine = '0.00' ). + assert_fine( iv_due = '20250110' iv_return = '20250110' iv_media = 'DVD' + iv_days = 0 iv_charge = 0 iv_fine = '0.00' ). + ENDMETHOD. + + METHOD book_in_grace. + assert_fine( iv_due = '20250101' iv_return = '20250108' iv_media = 'BOOK' + iv_days = 7 iv_charge = 0 iv_fine = '0.00' ). + ENDMETHOD. + + METHOD book_over_grace. + assert_fine( iv_due = '20250101' iv_return = '20250109' iv_media = 'BOOK' + iv_days = 8 iv_charge = 1 iv_fine = '0.30' ). + ENDMETHOD. + + METHOD dvd_in_grace. + assert_fine( iv_due = '20250101' iv_return = '20250103' iv_media = 'DVD' + iv_days = 2 iv_charge = 0 iv_fine = '0.00' ). + ENDMETHOD. + + METHOD dvd_over_grace. + assert_fine( iv_due = '20250101' iv_return = '20250104' iv_media = 'DVD' + iv_days = 3 iv_charge = 1 iv_fine = '1.50' ). + ENDMETHOD. + + METHOD game_no_grace. + assert_fine( iv_due = '20250101' iv_return = '20250102' iv_media = 'GAME' + iv_days = 1 iv_charge = 1 iv_fine = '2.50' ). + ENDMETHOD. + + METHOD reminder_fee_added. + assert_fine( iv_due = '20250101' iv_return = '20250215' iv_media = 'BOOK' + iv_days = 45 iv_charge = 38 iv_fine = '16.40' ). + ENDMETHOD. + + METHOD no_reminder_day_30. + assert_fine( iv_due = '20250101' iv_return = '20250131' iv_media = 'BOOK' + iv_days = 30 iv_charge = 23 iv_fine = '6.90' ). + ENDMETHOD. + + METHOD fine_capped. + assert_fine( iv_due = '20250101' iv_return = '20250113' iv_media = 'GAME' + iv_days = 12 iv_charge = 12 iv_fine = '25.00' ). + ENDMETHOD. + + METHOD cap_after_reminder. + assert_fine( iv_due = '20250101' iv_return = '20250201' iv_media = 'GAME' + iv_days = 31 iv_charge = 31 iv_fine = '25.00' ). + ENDMETHOD. + + METHOD unknown_media_no_fine. + assert_fine( iv_due = '20250101' iv_return = '20250105' iv_media = 'TAPE' + iv_days = 4 iv_charge = 0 iv_fine = '0.00' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/mutation.json b/tasks_gen/eval/G0103/mutation.json new file mode 100644 index 0000000..2800eda --- /dev/null +++ b/tasks_gen/eval/G0103/mutation.json @@ -0,0 +1,77 @@ +{ + "task": "G0103", + "mutants": [ + { + "object": "{{P}}OVERDUE_FINE", + "mutant": "line 41: <= -> > (rel)", + "status": "killed", + "hidden": "0/11", + "failed_tests": [ + "BOOK_IN_GRACE", + "BOOK_OVER_GRACE", + "CAP_AFTER_REMINDER", + "DVD_IN_GRACE", + "DVD_OVER_GRACE", + "FINE_CAPPED", + "GAME_NO_GRACE", + "NOT_LATE_NO_FINE", + "NO_REMINDER_DAY_30", + "REMINDER_FEE_ADDED", + "UNKNOWN_MEDIA_NO_FINE" + ] + }, + { + "object": "{{P}}OVERDUE_FINE", + "mutant": "line 51: - -> + (arith)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "BOOK_IN_GRACE", + "BOOK_OVER_GRACE", + "DVD_IN_GRACE", + "DVD_OVER_GRACE", + "NO_REMINDER_DAY_30", + "REMINDER_FEE_ADDED" + ] + }, + { + "object": "{{P}}OVERDUE_FINE", + "mutant": "line 53: * -> / (arith)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "BOOK_OVER_GRACE", + "CAP_AFTER_REMINDER", + "DVD_OVER_GRACE", + "FINE_CAPPED", + "GAME_NO_GRACE", + "NO_REMINDER_DAY_30", + "REMINDER_FEE_ADDED" + ] + }, + { + "object": "{{P}}OVERDUE_FINE", + "mutant": "line 55: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "REMINDER_FEE_ADDED" + ] + }, + { + "object": "{{P}}OVERDUE_FINE", + "mutant": "line 63: 7 -> 8 (const)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "BOOK_OVER_GRACE", + "NO_REMINDER_DAY_30", + "REMINDER_FEE_ADDED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0103/reference/overdue_fine.clas.abap b/tasks_gen/eval/G0103/reference/overdue_fine.clas.abap new file mode 100644 index 0000000..552c5be --- /dev/null +++ b/tasks_gen/eval/G0103/reference/overdue_fine.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}overdue_fine DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_media TYPE c LENGTH 8. + TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2. + TYPES: + BEGIN OF ty_result, + days_overdue TYPE i, + chargeable_days TYPE i, + fine TYPE ty_amount, + END OF ty_result. + + METHODS calculate + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_media_rule, + media TYPE ty_media, + rate TYPE ty_amount, + grace_days TYPE i, + END OF ty_media_rule, + tt_media_rule TYPE HASHED TABLE OF ty_media_rule WITH UNIQUE KEY media. + + CONSTANTS: + c_max_fine TYPE ty_amount VALUE '25.00', + c_reminder_fee TYPE ty_amount VALUE '5.00', + c_reminder_day TYPE i VALUE 30. + + METHODS media_rule + IMPORTING iv_media TYPE ty_media + RETURNING VALUE(rs_rule) TYPE ty_media_rule. +ENDCLASS. + + +CLASS {{p}}overdue_fine IMPLEMENTATION. + METHOD calculate. + rs_result-days_overdue = iv_return_date - iv_due_date. + IF rs_result-days_overdue <= 0. + CLEAR rs_result. + RETURN. + ENDIF. + + DATA(ls_rule) = media_rule( iv_media ). + IF ls_rule IS INITIAL. + RETURN. + ENDIF. + + rs_result-chargeable_days = nmax( val1 = rs_result-days_overdue - ls_rule-grace_days + val2 = 0 ). + rs_result-fine = rs_result-chargeable_days * ls_rule-rate. + IF rs_result-days_overdue > c_reminder_day. + rs_result-fine = rs_result-fine + c_reminder_fee. + ENDIF. + rs_result-fine = nmin( val1 = rs_result-fine + val2 = c_max_fine ). + ENDMETHOD. + + METHOD media_rule. + DATA(lt_rules) = VALUE tt_media_rule( + ( media = 'BOOK' rate = '0.30' grace_days = 7 ) + ( media = 'DVD' rate = '1.50' grace_days = 2 ) + ( media = 'GAME' rate = '2.50' grace_days = 0 ) ). + READ TABLE lt_rules INTO rs_rule WITH TABLE KEY media = iv_media. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/reference/overdue_fine.testclasses.abap b/tasks_gen/eval/G0103/reference/overdue_fine.testclasses.abap new file mode 100644 index 0000000..ea13767 --- /dev/null +++ b/tasks_gen/eval/G0103/reference/overdue_fine.testclasses.abap @@ -0,0 +1,89 @@ +CLASS ltc_overdue_fine DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}overdue_fine. + METHODS setup. + METHODS not_late_no_fine FOR TESTING. + METHODS book_in_grace FOR TESTING. + METHODS book_over_grace FOR TESTING. + METHODS dvd_over_grace FOR TESTING. + METHODS game_no_grace FOR TESTING. + METHODS reminder_fee FOR TESTING. + METHODS fine_is_capped FOR TESTING. + METHODS unknown_media FOR TESTING. +ENDCLASS. + + +CLASS ltc_overdue_fine IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}overdue_fine( ). + ENDMETHOD. + + METHOD not_late_no_fine. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250110' + iv_return_date = '20250109' + iv_media = 'BOOK' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '0.00' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD book_in_grace. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250108' + iv_media = 'BOOK' ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-chargeable_days ). + ENDMETHOD. + + METHOD book_over_grace. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250109' + iv_media = 'BOOK' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '0.30' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD dvd_over_grace. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250104' + iv_media = 'DVD' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '1.50' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD game_no_grace. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250102' + iv_media = 'GAME' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '2.50' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD reminder_fee. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250215' + iv_media = 'BOOK' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '16.40' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD fine_is_capped. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250113' + iv_media = 'GAME' ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}overdue_fine=>ty_amount( '25.00' ) + act = ls_result-fine ). + ENDMETHOD. + + METHOD unknown_media. + DATA(ls_result) = mo_cut->calculate( iv_due_date = '20250101' + iv_return_date = '20250105' + iv_media = 'TAPE' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_result-days_overdue ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-chargeable_days ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0103/spec.md b/tasks_gen/eval/G0103/spec.md new file mode 100644 index 0000000..b9ee012 --- /dev/null +++ b/tasks_gen/eval/G0103/spec.md @@ -0,0 +1,56 @@ +# 1. Goal +Calculate the overdue fine of a returned library item. The lending desk calls the +calculation before it prints the receipt. The result gives the days overdue, the +chargeable days, and the fine. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP contains no object for this task. +- The class is new. No other object is necessary. + +# 4. Contract +- Create the class {{P}}OVERDUE_FINE in package $TMP. +- The class is public and final. You can create it with NEW {{P}}OVERDUE_FINE( ) + without parameters. +- The class has these public types: + - TY_MEDIA: character field, length 8. + - TY_AMOUNT: packed number, length 8, 2 decimal places. + - TY_RESULT: structure with the components DAYS_OVERDUE (type i), + CHARGEABLE_DAYS (type i), and FINE (type TY_AMOUNT). +- The class has one public method: + CALCULATE + IMPORTING iv_due_date TYPE d + iv_return_date TYPE d + iv_media TYPE ty_media + RETURNING VALUE(rs_result) TYPE ty_result. +- Do not add other public methods or public components. + +# 5. Business rules +Apply the rules in this sequence. +1. If iv_return_date is earlier than or equal to iv_due_date, return + DAYS_OVERDUE = 0, CHARGEABLE_DAYS = 0, and FINE = 0.00. Stop. +2. DAYS_OVERDUE is the number of calendar days from iv_due_date to iv_return_date. +3. The media code gives the daily rate and the grace days: + - BOOK: daily rate 0.30, grace 7 days + - DVD: daily rate 1.50, grace 2 days + - GAME: daily rate 2.50, grace 0 days + Compare the media code exactly. If the media code is not in this list, return + CHARGEABLE_DAYS = 0 and FINE = 0.00. Keep DAYS_OVERDUE from rule 2. Stop. +4. CHARGEABLE_DAYS is DAYS_OVERDUE minus the grace days. If this value is less + than 0, CHARGEABLE_DAYS is 0. +5. FINE is CHARGEABLE_DAYS multiplied by the daily rate. +6. If DAYS_OVERDUE is greater than 30, add a reminder fee of 5.00 to FINE. +7. If FINE is greater than 25.00, FINE is 25.00. + +# 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 other object. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/eval/G0103/task.json b/tasks_gen/eval/G0103/task.json new file mode 100644 index 0000000..77f9809 --- /dev/null +++ b/tasks_gen/eval/G0103/task.json @@ -0,0 +1,41 @@ +{ + "id": "G0103", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}OVERDUE_FINE" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the overdue fine calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}OVERDUE_FINE", + "file": "reference/overdue_fine.clas.abap", + "description": "Overdue fine calculation for a library item", + "testclasses_file": "reference/overdue_fine.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0104/faulty/m0_insurance_premium.clas.abap b/tasks_gen/eval/G0104/faulty/m0_insurance_premium.clas.abap new file mode 100644 index 0000000..4ac7195 --- /dev/null +++ b/tasks_gen/eval/G0104/faulty/m0_insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + AND iv_power_kw <= 0 + OR iv_claim_free_years < 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base = 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + * get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 100 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/faulty/m1_insurance_premium.clas.abap b/tasks_gen/eval/G0104/faulty/m1_insurance_premium.clas.abap new file mode 100644 index 0000000..159e86f --- /dev/null +++ b/tasks_gen/eval/G0104/faulty/m1_insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + OR iv_power_kw <= 0 + OR iv_claim_free_years >= 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base = 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + * get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 100 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/faulty/m2_insurance_premium.clas.abap b/tasks_gen/eval/G0104/faulty/m2_insurance_premium.clas.abap new file mode 100644 index 0000000..c992d09 --- /dev/null +++ b/tasks_gen/eval/G0104/faulty/m2_insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + OR iv_power_kw <= 0 + OR iv_claim_free_years < 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base <> 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + * get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 100 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/faulty/m3_insurance_premium.clas.abap b/tasks_gen/eval/G0104/faulty/m3_insurance_premium.clas.abap new file mode 100644 index 0000000..57f97ee --- /dev/null +++ b/tasks_gen/eval/G0104/faulty/m3_insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + OR iv_power_kw <= 0 + OR iv_claim_free_years < 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base = 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + / get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 100 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/faulty/m4_insurance_premium.clas.abap b/tasks_gen/eval/G0104/faulty/m4_insurance_premium.clas.abap new file mode 100644 index 0000000..2607532 --- /dev/null +++ b/tasks_gen/eval/G0104/faulty/m4_insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + OR iv_power_kw <= 0 + OR iv_claim_free_years < 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base = 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + * get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 101 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/generation.json b/tasks_gen/eval/G0104/generation.json new file mode 100644 index 0000000..2da7a0d --- /dev/null +++ b/tasks_gen/eval/G0104/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G0104", + "pool": "eval", + "object_type": "CLAS", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 91.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/eval/G0104/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0104/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..46c09e3 --- /dev/null +++ b/tasks_gen/eval/G0104/hidden/t02_hidden.clas.abap @@ -0,0 +1,111 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}insurance_premium. + METHODS setup. + METHODS standard_premium FOR TESTING. + METHODS young_driver FOR TESTING. + METHODS senior_driver FOR TESTING. + METHODS high_power FOR TESTING. + METHODS very_high_power FOR TESTING. + METHODS low_power FOR TESTING. + METHODS claim_free_discount FOR TESTING. + METHODS claim_free_capped FOR TESTING. + METHODS minimum_premium FOR TESTING. + METHODS all_regions FOR TESTING. + METHODS invalid_region FOR TESTING. + METHODS invalid_inputs FOR TESTING. +ENDCLASS. + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}insurance_premium( ). + ENDMETHOD. + + METHOD standard_premium. + cl_abap_unit_assert=>assert_equals( + exp = 150 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD young_driver. + cl_abap_unit_assert=>assert_equals( + exp = 225 + act = mo_cut->calculate_premium( iv_age = 22 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD senior_driver. + cl_abap_unit_assert=>assert_equals( + exp = 180 + act = mo_cut->calculate_premium( iv_age = 70 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD high_power. + cl_abap_unit_assert=>assert_equals( + exp = 195 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 120 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD very_high_power. + cl_abap_unit_assert=>assert_equals( + exp = 240 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 200 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD low_power. + cl_abap_unit_assert=>assert_equals( + exp = 135 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 40 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD claim_free_discount. + cl_abap_unit_assert=>assert_equals( + exp = 180 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'B2' iv_claim_free_years = 2 ) ). + ENDMETHOD. + + METHOD claim_free_capped. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'B2' iv_claim_free_years = 10 ) ). + ENDMETHOD. + + METHOD minimum_premium. + cl_abap_unit_assert=>assert_equals( + exp = 100 + act = mo_cut->calculate_premium( iv_age = 25 iv_power_kw = 50 iv_region = 'A1' iv_claim_free_years = 6 ) ). + ENDMETHOD. + + METHOD all_regions. + cl_abap_unit_assert=>assert_equals( + exp = 250 msg = 'C3' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'C3' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 300 msg = 'D4' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'D4' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 350 msg = 'E5' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'E5' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD invalid_region. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'X9' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD invalid_inputs. + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'age too low' + act = mo_cut->calculate_premium( iv_age = 17 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'age too high' + act = mo_cut->calculate_premium( iv_age = 100 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'power too low' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 0 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'negative claim free years' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = -1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/mutation.json b/tasks_gen/eval/G0104/mutation.json new file mode 100644 index 0000000..9a90de9 --- /dev/null +++ b/tasks_gen/eval/G0104/mutation.json @@ -0,0 +1,83 @@ +{ + "task": "G0104", + "mutants": [ + { + "object": "{{P}}INSURANCE_PREMIUM", + "mutant": "line 30: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "INVALID_INPUTS" + ] + }, + { + "object": "{{P}}INSURANCE_PREMIUM", + "mutant": "line 31: < -> >= (rel)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "ALL_REGIONS", + "CLAIM_FREE_CAPPED", + "CLAIM_FREE_DISCOUNT", + "HIGH_POWER", + "INVALID_INPUTS", + "LOW_POWER", + "MINIMUM_PREMIUM", + "SENIOR_DRIVER", + "STANDARD_PREMIUM", + "VERY_HIGH_POWER", + "YOUNG_DRIVER" + ] + }, + { + "object": "{{P}}INSURANCE_PREMIUM", + "mutant": "line 36: = -> <> (eq)", + "status": "killed", + "hidden": "1/12", + "failed_tests": [ + "ALL_REGIONS", + "CLAIM_FREE_CAPPED", + "CLAIM_FREE_DISCOUNT", + "HIGH_POWER", + "INVALID_REGION", + "LOW_POWER", + "MINIMUM_PREMIUM", + "SENIOR_DRIVER", + "STANDARD_PREMIUM", + "VERY_HIGH_POWER", + "YOUNG_DRIVER" + ] + }, + { + "object": "{{P}}INSURANCE_PREMIUM", + "mutant": "line 41: * -> / (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "SENIOR_DRIVER", + "YOUNG_DRIVER" + ] + }, + { + "object": "{{P}}INSURANCE_PREMIUM", + "mutant": "line 94: 100 -> 101 (const)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "ALL_REGIONS", + "CLAIM_FREE_CAPPED", + "CLAIM_FREE_DISCOUNT", + "HIGH_POWER", + "LOW_POWER", + "SENIOR_DRIVER", + "STANDARD_PREMIUM", + "VERY_HIGH_POWER", + "YOUNG_DRIVER" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0104/reference/insurance_premium.clas.abap b/tasks_gen/eval/G0104/reference/insurance_premium.clas.abap new file mode 100644 index 0000000..8c50841 --- /dev/null +++ b/tasks_gen/eval/G0104/reference/insurance_premium.clas.abap @@ -0,0 +1,96 @@ +CLASS {{p}}insurance_premium DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_region TYPE c LENGTH 2, + ty_premium TYPE p LENGTH 8 DECIMALS 2. + METHODS calculate_premium + IMPORTING iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING VALUE(rv_premium) TYPE ty_premium. + PRIVATE SECTION. + METHODS get_base_premium + IMPORTING iv_region TYPE ty_region + RETURNING VALUE(rv_base) TYPE ty_premium. + METHODS get_age_factor + IMPORTING iv_age TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_power_factor + IMPORTING iv_power_kw TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. + METHODS get_discount_factor + IMPORTING iv_claim_free_years TYPE i + RETURNING VALUE(rv_factor) TYPE decfloat34. +ENDCLASS. + +CLASS {{p}}insurance_premium IMPLEMENTATION. + METHOD calculate_premium. + IF iv_age < 18 OR iv_age > 99 + OR iv_power_kw <= 0 + OR iv_claim_free_years < 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_base) = get_base_premium( iv_region ). + IF lv_base = 0. + rv_premium = 0. + RETURN. + ENDIF. + DATA(lv_premium) = CONV decfloat34( lv_base ) + * get_age_factor( iv_age ) + * get_power_factor( iv_power_kw ) + * get_discount_factor( iv_claim_free_years ). + rv_premium = lv_premium. + IF rv_premium < 100. + rv_premium = 100. + ENDIF. + ENDMETHOD. + + METHOD get_base_premium. + CASE iv_region. + WHEN 'A1'. + rv_base = 150. + WHEN 'B2'. + rv_base = 200. + WHEN 'C3'. + rv_base = 250. + WHEN 'D4'. + rv_base = 300. + WHEN 'E5'. + rv_base = 350. + WHEN OTHERS. + rv_base = 0. + ENDCASE. + ENDMETHOD. + + METHOD get_age_factor. + IF iv_age < 25. + rv_factor = '1.5'. + ELSEIF iv_age > 65. + rv_factor = '1.2'. + ELSE. + rv_factor = '1.0'. + ENDIF. + ENDMETHOD. + + METHOD get_power_factor. + IF iv_power_kw <= 50. + rv_factor = '0.9'. + ELSEIF iv_power_kw <= 100. + rv_factor = '1.0'. + ELSEIF iv_power_kw <= 150. + rv_factor = '1.3'. + ELSE. + rv_factor = '1.6'. + ENDIF. + ENDMETHOD. + + METHOD get_discount_factor. + DATA(lv_discount) = iv_claim_free_years * 5. + IF lv_discount > 30. + lv_discount = 30. + ENDIF. + rv_factor = CONV decfloat34( 100 - lv_discount ) / 100. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/reference/insurance_premium.testclasses.abap b/tasks_gen/eval/G0104/reference/insurance_premium.testclasses.abap new file mode 100644 index 0000000..9d6b32f --- /dev/null +++ b/tasks_gen/eval/G0104/reference/insurance_premium.testclasses.abap @@ -0,0 +1,110 @@ +CLASS ltc_insurance_premium DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}insurance_premium. + METHODS setup. + METHODS standard_premium FOR TESTING. + METHODS young_driver FOR TESTING. + METHODS senior_driver FOR TESTING. + METHODS high_power FOR TESTING. + METHODS very_high_power FOR TESTING. + METHODS low_power FOR TESTING. + METHODS claim_free_discount FOR TESTING. + METHODS claim_free_capped FOR TESTING. + METHODS minimum_premium FOR TESTING. + METHODS all_regions FOR TESTING. + METHODS invalid_region FOR TESTING. + METHODS invalid_inputs FOR TESTING. +ENDCLASS. + +CLASS ltc_insurance_premium IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}insurance_premium( ). + ENDMETHOD. + + METHOD standard_premium. + cl_abap_unit_assert=>assert_equals( + exp = 150 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD young_driver. + cl_abap_unit_assert=>assert_equals( + exp = 225 + act = mo_cut->calculate_premium( iv_age = 22 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD senior_driver. + cl_abap_unit_assert=>assert_equals( + exp = 180 + act = mo_cut->calculate_premium( iv_age = 70 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD high_power. + cl_abap_unit_assert=>assert_equals( + exp = 195 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 120 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD very_high_power. + cl_abap_unit_assert=>assert_equals( + exp = 240 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 200 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD low_power. + cl_abap_unit_assert=>assert_equals( + exp = 135 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 40 iv_region = 'A1' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD claim_free_discount. + cl_abap_unit_assert=>assert_equals( + exp = 180 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'B2' iv_claim_free_years = 2 ) ). + ENDMETHOD. + + METHOD claim_free_capped. + cl_abap_unit_assert=>assert_equals( + exp = 140 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'B2' iv_claim_free_years = 10 ) ). + ENDMETHOD. + + METHOD minimum_premium. + cl_abap_unit_assert=>assert_equals( + exp = 100 + act = mo_cut->calculate_premium( iv_age = 25 iv_power_kw = 50 iv_region = 'A1' iv_claim_free_years = 6 ) ). + ENDMETHOD. + + METHOD all_regions. + cl_abap_unit_assert=>assert_equals( + exp = 250 msg = 'C3' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'C3' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 300 msg = 'D4' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'D4' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 350 msg = 'E5' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'E5' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD invalid_region. + cl_abap_unit_assert=>assert_equals( + exp = 0 + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'X9' iv_claim_free_years = 0 ) ). + ENDMETHOD. + + METHOD invalid_inputs. + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'age too low' + act = mo_cut->calculate_premium( iv_age = 17 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'age too high' + act = mo_cut->calculate_premium( iv_age = 100 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'power too low' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 0 iv_region = 'A1' iv_claim_free_years = 0 ) ). + cl_abap_unit_assert=>assert_equals( + exp = 0 msg = 'negative claim free years' + act = mo_cut->calculate_premium( iv_age = 30 iv_power_kw = 80 iv_region = 'A1' iv_claim_free_years = -1 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0104/spec.md b/tasks_gen/eval/G0104/spec.md new file mode 100644 index 0000000..6f334f8 --- /dev/null +++ b/tasks_gen/eval/G0104/spec.md @@ -0,0 +1,63 @@ +# 1. Goal +Calculate the annual premium of a car insurance policy. An insurance program calls the calculation before it sends an offer to a customer. The program needs one premium for each policy. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP. +- No other objects are required. + +# 4. Contract +- Create the class {{P}}INSURANCE_PREMIUM in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class has the public type TY_REGION (CHAR 2) and TY_PREMIUM (P LENGTH 8 DECIMALS 2). +- The class has one public method: + CALCULATE_PREMIUM + IMPORTING + iv_age TYPE i + iv_power_kw TYPE i + iv_region TYPE ty_region + iv_claim_free_years TYPE i + RETURNING + VALUE(rv_premium) TYPE ty_premium. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. +1. Validate the inputs: + - iv_age must be between 18 and 99. + - iv_power_kw must be greater than 0. + - iv_claim_free_years must not be negative. + - iv_region must be one of the known regions (see rule 2). + If any check fails, return 0. +2. The base premium depends on the region: + A1: 150, B2: 200, C3: 250, D4: 300, E5: 350. +3. The age factor: + - age < 25: 1.5 + - 25 <= age <= 65: 1.0 + - age > 65: 1.2 +4. The power factor: + - power <= 50 kW: 0.9 + - 51 <= power <= 100 kW: 1.0 + - 101 <= power <= 150 kW: 1.3 + - power > 150 kW: 1.6 +5. The claim-free discount: + - Each claim-free year gives a 5% discount. + - The maximum discount is 30%. + - The discount factor is 1 minus the discount (e.g., 0 claim-free years: 1.0, 6 or more: 0.7). +6. Calculate the premium: + premium = base premium * age factor * power factor * discount factor. + Calculate with full precision and round the final result to 2 decimals. +7. If the rounded premium is less than 100, return 100. +8. Else return the rounded premium. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Method length below 40 statements. No global variables. No comment that restates the code. One statement per line. +- 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/eval/G0104/task.json b/tasks_gen/eval/G0104/task.json new file mode 100644 index 0000000..c62db27 --- /dev/null +++ b/tasks_gen/eval/G0104/task.json @@ -0,0 +1,40 @@ +{ + "id": "G0104", + "category": "A", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}INSURANCE_PREMIUM" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}INSURANCE_PREMIUM", + "file": "reference/insurance_premium.clas.abap", + "description": "Insurance premium calculator", + "testclasses_file": "reference/insurance_premium.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0105/faulty/m0_toll_calc.clas.abap b/tasks_gen/eval/G0105/faulty/m0_toll_calc.clas.abap new file mode 100644 index 0000000..0c2eb7f --- /dev/null +++ b/tasks_gen/eval/G0105/faulty/m0_toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent - surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up ). + + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 1 OR iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/faulty/m1_toll_calc.clas.abap b/tasks_gen/eval/G0105/faulty/m1_toll_calc.clas.abap new file mode 100644 index 0000000..549616a --- /dev/null +++ b/tasks_gen/eval/G0105/faulty/m1_toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent + surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 3 mode = cl_abap_math=>round_half_up ). + + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 1 OR iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/faulty/m2_toll_calc.clas.abap b/tasks_gen/eval/G0105/faulty/m2_toll_calc.clas.abap new file mode 100644 index 0000000..8a6adf7 --- /dev/null +++ b/tasks_gen/eval/G0105/faulty/m2_toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent + surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up ). + + IF rv_fee >= c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 1 OR iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/faulty/m3_toll_calc.clas.abap b/tasks_gen/eval/G0105/faulty/m3_toll_calc.clas.abap new file mode 100644 index 0000000..44c14b8 --- /dev/null +++ b/tasks_gen/eval/G0105/faulty/m3_toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent + surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up ). + + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 2 OR iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/faulty/m4_toll_calc.clas.abap b/tasks_gen/eval/G0105/faulty/m4_toll_calc.clas.abap new file mode 100644 index 0000000..1c2b07b --- /dev/null +++ b/tasks_gen/eval/G0105/faulty/m4_toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent + surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up ). + + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 1 AND iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/generation.json b/tasks_gen/eval/G0105/generation.json new file mode 100644 index 0000000..9bd4d54 --- /dev/null +++ b/tasks_gen/eval/G0105/generation.json @@ -0,0 +1,33 @@ +{ + "id": "G0105", + "pool": "eval", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "reference/toll_calc.testclasses.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"" + ], + "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/eval/G0105/hidden/t14_hidden.clas.abap b/tasks_gen/eval/G0105/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..9c5c2f8 --- /dev/null +++ b/tasks_gen/eval/G0105/hidden/t14_hidden.clas.abap @@ -0,0 +1,140 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_km TYPE decfloat34 + iv_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS is_rejected + IMPORTING iv_km TYPE decfloat34 + iv_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_rejected) TYPE abap_bool. + METHODS fee_two_axles FOR TESTING. + METHODS fee_three_axles FOR TESTING. + METHODS fee_four_axles FOR TESTING. + METHODS fee_five_axles FOR TESTING. + METHODS rounding_half_up FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS rejects_negative_km FOR TESTING. + METHODS rejects_bad_class FOR TESTING. + METHODS rejects_bad_axles FOR TESTING. + METHODS func_returns_fee FOR TESTING. + METHODS func_rejects_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + + METHOD fee_for. + TRY. + rv_fee = NEW {{p}}toll_calc( )->calculate( + iv_distance_km = iv_km + iv_weight_class = iv_class + iv_axles = iv_axles ). + CATCH cx_abap_invalid_value. + rv_fee = -1. + ENDTRY. + ENDMETHOD. + + METHOD is_rejected. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = iv_km iv_class = iv_class iv_axles = iv_axles ). + rv_rejected = boolc( lv_fee < 0 ). + ENDMETHOD. + + METHOD fee_two_axles. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 100 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD fee_three_axles. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 100 iv_class = 2 iv_axles = 3 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '14.70' ) act = lv_fee ). + ENDMETHOD. + + METHOD fee_four_axles. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 100 iv_class = 3 iv_axles = 4 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '19.80' ) act = lv_fee ). + ENDMETHOD. + + METHOD fee_five_axles. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 100 iv_class = 4 iv_axles = 5 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.30' ) act = lv_fee ). + ENDMETHOD. + + METHOD rounding_half_up. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 25 iv_class = 1 iv_axles = 3 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '2.63' ) act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 5 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) act = lv_fee ). + lv_fee = fee_for( iv_km = 0 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD rejects_negative_km. + DATA lv_rejected TYPE abap_bool. + lv_rejected = is_rejected( iv_km = -1 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + ENDMETHOD. + + METHOD rejects_bad_class. + DATA lv_rejected TYPE abap_bool. + lv_rejected = is_rejected( iv_km = 100 iv_class = 0 iv_axles = 2 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + lv_rejected = is_rejected( iv_km = 100 iv_class = 5 iv_axles = 2 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + ENDMETHOD. + + METHOD rejects_bad_axles. + DATA lv_rejected TYPE abap_bool. + lv_rejected = is_rejected( iv_km = 100 iv_class = 1 iv_axles = 1 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + lv_rejected = is_rejected( iv_km = 100 iv_class = 1 iv_axles = 6 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + ENDMETHOD. + + METHOD func_returns_fee. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}TOLL_FEE' + EXPORTING + iv_distance_km = 100 + iv_weight_class = 3 + iv_axles = 4 + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '19.80' ) act = lv_fee ). + ENDMETHOD. + + METHOD func_rejects_input. + DATA lv_fee TYPE decfloat34. + CALL FUNCTION '{{P}}TOLL_FEE' + EXPORTING + iv_distance_km = -1 + iv_weight_class = 1 + iv_axles = 2 + IMPORTING + ev_fee = lv_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + cl_abap_unit_assert=>assert_initial( act = lv_fee ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/mutation.json b/tasks_gen/eval/G0105/mutation.json new file mode 100644 index 0000000..f510c9e --- /dev/null +++ b/tasks_gen/eval/G0105/mutation.json @@ -0,0 +1,66 @@ +{ + "task": "G0105", + "mutants": [ + { + "object": "{{P}}TOLL_CALC", + "mutant": "line 50: + -> - (arith)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "FEE_FIVE_AXLES", + "FEE_FOUR_AXLES", + "FEE_THREE_AXLES", + "FUNC_RETURNS_FEE", + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}TOLL_CALC", + "mutant": "line 51: 2 -> 3 (const)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}TOLL_CALC", + "mutant": "line 53: < -> >= (rel)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "FEE_FIVE_AXLES", + "FEE_FOUR_AXLES", + "FEE_THREE_AXLES", + "FEE_TWO_AXLES", + "FUNC_RETURNS_FEE", + "MINIMUM_FEE", + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}TOLL_CALC", + "mutant": "line 62: 1 -> 2 (const)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "FEE_TWO_AXLES", + "MINIMUM_FEE", + "ROUNDING_HALF_UP" + ] + }, + { + "object": "{{P}}TOLL_CALC", + "mutant": "line 62: OR -> AND (logic)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "REJECTS_BAD_CLASS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0105/reference/t14_test.clas.abap b/tasks_gen/eval/G0105/reference/t14_test.clas.abap new file mode 100644 index 0000000..dc477ed --- /dev/null +++ b/tasks_gen/eval/G0105/reference/t14_test.clas.abap @@ -0,0 +1,62 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS call_fee + IMPORTING iv_km TYPE decfloat34 + iv_class TYPE i + iv_axles TYPE i + EXPORTING ev_fee TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS returns_fee FOR TESTING. + METHODS rejects_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + + METHOD call_fee. + CALL FUNCTION '{{P}}TOLL_FEE' + EXPORTING + iv_distance_km = iv_km + iv_weight_class = iv_class + iv_axles = iv_axles + IMPORTING + ev_fee = ev_fee + EXCEPTIONS + invalid_input = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD returns_fee. + DATA lv_fee TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call_fee( + EXPORTING + iv_km = 100 + iv_class = 2 + iv_axles = 3 + IMPORTING + ev_fee = lv_fee + ev_subrc = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '14.70' ) act = lv_fee ). + ENDMETHOD. + + METHOD rejects_input. + DATA lv_fee TYPE decfloat34. + DATA lv_rc TYPE sysubrc. + call_fee( + EXPORTING + iv_km = -1 + iv_class = 1 + iv_axles = 2 + 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_initial( act = lv_fee ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/reference/toll_calc.clas.abap b/tasks_gen/eval/G0105/reference/toll_calc.clas.abap new file mode 100644 index 0000000..2c23d47 --- /dev/null +++ b/tasks_gen/eval/G0105/reference/toll_calc.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}toll_calc DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + PRIVATE SECTION. + CONSTANTS c_percent TYPE decfloat34 VALUE '100'. + CONSTANTS c_min_fee TYPE decfloat34 VALUE '1.00'. + CONSTANTS: + BEGIN OF c_rate, + class_1 TYPE decfloat34 VALUE '0.10', + class_2 TYPE decfloat34 VALUE '0.14', + class_3 TYPE decfloat34 VALUE '0.18', + class_4 TYPE decfloat34 VALUE '0.22', + END OF c_rate. + CONSTANTS: + BEGIN OF c_surcharge, + axles_2 TYPE decfloat34 VALUE '0', + axles_3 TYPE decfloat34 VALUE '5', + axles_4 TYPE decfloat34 VALUE '10', + axles_5 TYPE decfloat34 VALUE '15', + END OF c_surcharge. + + METHODS check_input + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RAISING cx_abap_invalid_value. + METHODS base_rate + IMPORTING iv_weight_class TYPE i + RETURNING VALUE(rv_rate) TYPE decfloat34. + METHODS surcharge + IMPORTING iv_axles TYPE i + RETURNING VALUE(rv_percent) TYPE decfloat34. +ENDCLASS. + + +CLASS {{p}}toll_calc IMPLEMENTATION. + + METHOD calculate. + check_input( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + + DATA(lv_amount) = iv_distance_km * base_rate( iv_weight_class ). + lv_amount = lv_amount * ( c_percent + surcharge( iv_axles ) ) / c_percent. + rv_fee = round( val = lv_amount dec = 2 mode = cl_abap_math=>round_half_up ). + + IF rv_fee < c_min_fee. + rv_fee = c_min_fee. + ENDIF. + ENDMETHOD. + + METHOD check_input. + IF iv_distance_km < 0. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_weight_class < 1 OR iv_weight_class > 4. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + IF iv_axles < 2 OR iv_axles > 5. + RAISE EXCEPTION NEW cx_abap_invalid_value( ). + ENDIF. + ENDMETHOD. + + METHOD base_rate. + CASE iv_weight_class. + WHEN 1. + rv_rate = c_rate-class_1. + WHEN 2. + rv_rate = c_rate-class_2. + WHEN 3. + rv_rate = c_rate-class_3. + WHEN 4. + rv_rate = c_rate-class_4. + WHEN OTHERS. + rv_rate = 0. + ENDCASE. + ENDMETHOD. + + METHOD surcharge. + CASE iv_axles. + WHEN 2. + rv_percent = c_surcharge-axles_2. + WHEN 3. + rv_percent = c_surcharge-axles_3. + WHEN 4. + rv_percent = c_surcharge-axles_4. + WHEN 5. + rv_percent = c_surcharge-axles_5. + WHEN OTHERS. + rv_percent = 0. + ENDCASE. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/reference/toll_calc.testclasses.abap b/tasks_gen/eval/G0105/reference/toll_calc.testclasses.abap new file mode 100644 index 0000000..853d3c8 --- /dev/null +++ b/tasks_gen/eval/G0105/reference/toll_calc.testclasses.abap @@ -0,0 +1,65 @@ +CLASS ltc_toll_calc DEFINITION FINAL FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + METHODS fee_for + IMPORTING iv_km TYPE decfloat34 + iv_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34. + METHODS is_rejected + IMPORTING iv_km TYPE decfloat34 + iv_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_rejected) TYPE abap_bool. + METHODS rate_and_surcharge FOR TESTING. + METHODS minimum_fee FOR TESTING. + METHODS rejects_invalid FOR TESTING. +ENDCLASS. + + +CLASS ltc_toll_calc IMPLEMENTATION. + + METHOD fee_for. + TRY. + rv_fee = NEW {{p}}toll_calc( )->calculate( + iv_distance_km = iv_km + iv_weight_class = iv_class + iv_axles = iv_axles ). + CATCH cx_abap_invalid_value. + rv_fee = -1. + ENDTRY. + ENDMETHOD. + + METHOD is_rejected. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = iv_km iv_class = iv_class iv_axles = iv_axles ). + rv_rejected = boolc( lv_fee < 0 ). + ENDMETHOD. + + METHOD rate_and_surcharge. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 100 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.00' ) act = lv_fee ). + lv_fee = fee_for( iv_km = 100 iv_class = 4 iv_axles = 5 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '25.30' ) act = lv_fee ). + ENDMETHOD. + + METHOD minimum_fee. + DATA lv_fee TYPE decfloat34. + lv_fee = fee_for( iv_km = 1 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '1.00' ) act = lv_fee ). + ENDMETHOD. + + METHOD rejects_invalid. + DATA lv_rejected TYPE abap_bool. + lv_rejected = is_rejected( iv_km = -1 iv_class = 1 iv_axles = 2 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + lv_rejected = is_rejected( iv_km = 100 iv_class = 5 iv_axles = 2 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + lv_rejected = is_rejected( iv_km = 100 iv_class = 1 iv_axles = 6 ). + cl_abap_unit_assert=>assert_true( act = lv_rejected ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0105/reference/toll_fee.func.abap b/tasks_gen/eval/G0105/reference/toll_fee.func.abap new file mode 100644 index 0000000..36abfdd --- /dev/null +++ b/tasks_gen/eval/G0105/reference/toll_fee.func.abap @@ -0,0 +1,20 @@ +FUNCTION {{p}}toll_fee + IMPORTING + iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_input. + + TRY. + ev_fee = NEW {{p}}toll_calc( )->calculate( + iv_distance_km = iv_distance_km + iv_weight_class = iv_weight_class + iv_axles = iv_axles ). + CATCH cx_abap_invalid_value. + RAISE invalid_input. + ENDTRY. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0105/spec.md b/tasks_gen/eval/G0105/spec.md new file mode 100644 index 0000000..94ce1ec --- /dev/null +++ b/tasks_gen/eval/G0105/spec.md @@ -0,0 +1,86 @@ +# 1. Goal + +A toll operator charges a fee for each truck trip on its road network. The trip +log program calls one function module for each trip. The call happens before +the program stores the trip. The fee depends only on the trip data that the +caller passes. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_TOLL exists in package $TMP. +- All objects of this task are in package $TMP. +- The trip log program is not part of this task. + +# 4. Contract + +## 4.1 Function module {{P}}TOLL_FEE + +Create the function module {{P}}TOLL_FEE in function group {{P}}FG_TOLL. +The interface is exactly: + + IMPORTING + iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + EXPORTING + ev_fee TYPE decfloat34 + EXCEPTIONS + invalid_input + +## 4.2 Class {{P}}TOLL_CALC + +Create the global class {{P}}TOLL_CALC in package $TMP. The class is public and +final. The class has a public instance constructor without parameters. +The class has this public method: + + METHODS calculate + IMPORTING iv_distance_km TYPE decfloat34 + iv_weight_class TYPE i + iv_axles TYPE i + RETURNING VALUE(rv_fee) TYPE decfloat34 + RAISING cx_abap_invalid_value. + +# 5. Business rules + +1. The distance is the length of the trip in kilometres. A distance below 0 + is invalid input. +2. The weight class is 1, 2, 3, or 4. Every other value is invalid input. +3. The number of axles is 2, 3, 4, or 5. Every other value is invalid input. +4. Invalid input raises the exception INVALID_INPUT in {{P}}TOLL_FEE and the + exception CX_ABAP_INVALID_VALUE in {{P}}TOLL_CALC. {{P}}TOLL_FEE does not + set the fee in this case. +5. The base rate per kilometre depends on the weight class: + - Weight class 1: 0.10 + - Weight class 2: 0.14 + - Weight class 3: 0.18 + - Weight class 4: 0.22 +6. The number of axles adds a surcharge to the base amount: + - 2 axles: 0 % + - 3 axles: 5 % + - 4 axles: 10 % + - 5 axles: 15 % +7. The base amount is the distance multiplied by the base rate. The fee is the + base amount increased by the surcharge percentage. Round the fee half up to + 2 decimals. +8. The minimum fee is 1.00. If the fee from rule 7 is below 1.00, the fee is + 1.00. +9. {{P}}TOLL_FEE returns the fee that {{P}}TOLL_CALC calculates. + +# 6. Constraints + +- Release target: 7.58. +- The calculation is pure logic. It reads no database table and no other + external data. +- Out of scope: the trip log program and every object that is not in the + contract. + +# 7. Acceptance + +- {{P}}TOLL_FEE and {{P}}TOLL_CALC are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests. Put local test classes in {{P}}TOLL_CALC. Write a + global test class for {{P}}TOLL_FEE. diff --git a/tasks_gen/eval/G0105/task.json b/tasks_gen/eval/G0105/task.json new file mode 100644 index 0000000..e678d17 --- /dev/null +++ b/tasks_gen/eval/G0105/task.json @@ -0,0 +1,82 @@ +{ + "id": "G0105", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_TOLL", + "description": "Toll fee function group" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}TOLL_FEE", + "functionGroup": "{{P}}FG_TOLL", + "params": [ + { + "name": "iv_distance_km", + "type": "decfloat34" + }, + { + "name": "iv_weight_class", + "type": "i" + }, + { + "name": "iv_axles", + "type": "i" + }, + { + "name": "ev_fee", + "type": "decfloat34" + } + ] + }, + { + "type": "CLAS", + "name": "{{P}}TOLL_CALC" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOLL_CALC", + "file": "reference/toll_calc.clas.abap", + "testclasses_file": "reference/toll_calc.testclasses.abap", + "description": "Toll fee calculation" + }, + { + "type": "FUNC", + "name": "{{P}}TOLL_FEE", + "functionGroup": "{{P}}FG_TOLL", + "file": "reference/toll_fee.func.abap", + "description": "Toll fee function module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0106/faulty/m0_app_scorer.clas.abap b/tasks_gen/eval/G0106/faulty/m0_app_scorer.clas.abap new file mode 100644 index 0000000..8f4e6a6 --- /dev/null +++ b/tasks_gen/eval/G0106/faulty/m0_app_scorer.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}app_scorer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 4. + + TYPES: BEGIN OF ty_result, + status TYPE ty_status, + score TYPE i, + rating TYPE string, + END OF ty_result. + + CONSTANTS: BEGIN OF co_status, + ok TYPE ty_status VALUE 'OK', + bad_years TYPE ty_status VALUE 'YEAR', + bad_match TYPE ty_status VALUE 'MATC', + bad_degree TYPE ty_status VALUE 'DEGR', + END OF co_status. + + METHODS evaluate + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + RETURNING + VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + METHODS experience_points + IMPORTING iv_years TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS education_points + IMPORTING iv_degree TYPE string + RETURNING VALUE(rv_points) TYPE i. + METHODS skill_points + IMPORTING iv_match TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS rating_for + IMPORTING iv_score TYPE i + RETURNING VALUE(rv_rating) TYPE string. +ENDCLASS. + + +CLASS {{p}}app_scorer IMPLEMENTATION. + + METHOD evaluate. + rs_result = VALUE #( status = co_status-ok ). + + IF iv_years < 0. + rs_result-status = co_status-bad_years. + RETURN. + ENDIF. + + IF iv_match < 0 AND iv_match > 100. + rs_result-status = co_status-bad_match. + RETURN. + ENDIF. + + DATA(lv_education) = education_points( iv_degree ). + IF lv_education < 0. + rs_result-status = co_status-bad_degree. + RETURN. + ENDIF. + + rs_result-score = experience_points( iv_years ) + lv_education + skill_points( iv_match ). + rs_result-rating = rating_for( rs_result-score ). + ENDMETHOD. + + METHOD experience_points. + rv_points = iv_years * 8. + IF rv_points > 40. + rv_points = 40. + ENDIF. + ENDMETHOD. + + METHOD education_points. + CASE iv_degree. + WHEN 'PHD'. + rv_points = 30. + WHEN 'MAS'. + rv_points = 25. + WHEN 'BAC'. + rv_points = 20. + WHEN 'VOC'. + rv_points = 10. + WHEN OTHERS. + rv_points = -1. + ENDCASE. + ENDMETHOD. + + METHOD skill_points. + rv_points = ( iv_match * 30 + 50 ) DIV 100. + ENDMETHOD. + + METHOD rating_for. + IF iv_score >= 75. + rv_rating = 'INVITE'. + ELSEIF iv_score >= 50. + rv_rating = 'REVIEW'. + ELSE. + rv_rating = 'REJECT'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/faulty/m1_appl_score.func.abap b/tasks_gen/eval/G0106/faulty/m1_appl_score.func.abap new file mode 100644 index 0000000..9723b04 --- /dev/null +++ b/tasks_gen/eval/G0106/faulty/m1_appl_score.func.abap @@ -0,0 +1,31 @@ +FUNCTION {{p}}appl_score + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + EXPORTING + ev_score TYPE i + ev_rating TYPE string + EXCEPTIONS + invalid_years + invalid_match + invalid_degree. + + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( + iv_years = iv_years + iv_degree = iv_degree + iv_match = iv_match ). + + IF ls_result-status <> {{p}}app_scorer=>co_status-bad_years. + RAISE invalid_years. + ELSEIF ls_result-status = {{p}}app_scorer=>co_status-bad_match. + RAISE invalid_match. + ELSEIF ls_result-status = {{p}}app_scorer=>co_status-bad_degree. + RAISE invalid_degree. + ENDIF. + + ev_score = ls_result-score. + ev_rating = ls_result-rating. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0106/faulty/m2_app_scorer.clas.abap b/tasks_gen/eval/G0106/faulty/m2_app_scorer.clas.abap new file mode 100644 index 0000000..0df7dff --- /dev/null +++ b/tasks_gen/eval/G0106/faulty/m2_app_scorer.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}app_scorer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 4. + + TYPES: BEGIN OF ty_result, + status TYPE ty_status, + score TYPE i, + rating TYPE string, + END OF ty_result. + + CONSTANTS: BEGIN OF co_status, + ok TYPE ty_status VALUE 'OK', + bad_years TYPE ty_status VALUE 'YEAR', + bad_match TYPE ty_status VALUE 'MATC', + bad_degree TYPE ty_status VALUE 'DEGR', + END OF co_status. + + METHODS evaluate + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + RETURNING + VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + METHODS experience_points + IMPORTING iv_years TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS education_points + IMPORTING iv_degree TYPE string + RETURNING VALUE(rv_points) TYPE i. + METHODS skill_points + IMPORTING iv_match TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS rating_for + IMPORTING iv_score TYPE i + RETURNING VALUE(rv_rating) TYPE string. +ENDCLASS. + + +CLASS {{p}}app_scorer IMPLEMENTATION. + + METHOD evaluate. + rs_result = VALUE #( status = co_status-ok ). + + IF iv_years < 0. + rs_result-status = co_status-bad_years. + RETURN. + ENDIF. + + IF iv_match < 0 OR iv_match <= 100. + rs_result-status = co_status-bad_match. + RETURN. + ENDIF. + + DATA(lv_education) = education_points( iv_degree ). + IF lv_education < 0. + rs_result-status = co_status-bad_degree. + RETURN. + ENDIF. + + rs_result-score = experience_points( iv_years ) + lv_education + skill_points( iv_match ). + rs_result-rating = rating_for( rs_result-score ). + ENDMETHOD. + + METHOD experience_points. + rv_points = iv_years * 8. + IF rv_points > 40. + rv_points = 40. + ENDIF. + ENDMETHOD. + + METHOD education_points. + CASE iv_degree. + WHEN 'PHD'. + rv_points = 30. + WHEN 'MAS'. + rv_points = 25. + WHEN 'BAC'. + rv_points = 20. + WHEN 'VOC'. + rv_points = 10. + WHEN OTHERS. + rv_points = -1. + ENDCASE. + ENDMETHOD. + + METHOD skill_points. + rv_points = ( iv_match * 30 + 50 ) DIV 100. + ENDMETHOD. + + METHOD rating_for. + IF iv_score >= 75. + rv_rating = 'INVITE'. + ELSEIF iv_score >= 50. + rv_rating = 'REVIEW'. + ELSE. + rv_rating = 'REJECT'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/faulty/m3_appl_score.func.abap b/tasks_gen/eval/G0106/faulty/m3_appl_score.func.abap new file mode 100644 index 0000000..b1bd2dd --- /dev/null +++ b/tasks_gen/eval/G0106/faulty/m3_appl_score.func.abap @@ -0,0 +1,31 @@ +FUNCTION {{p}}appl_score + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + EXPORTING + ev_score TYPE i + ev_rating TYPE string + EXCEPTIONS + invalid_years + invalid_match + invalid_degree. + + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( + iv_years = iv_years + iv_degree = iv_degree + iv_match = iv_match ). + + IF ls_result-status = {{p}}app_scorer=>co_status-bad_years. + RAISE invalid_years. + ELSEIF ls_result-status <> {{p}}app_scorer=>co_status-bad_match. + RAISE invalid_match. + ELSEIF ls_result-status = {{p}}app_scorer=>co_status-bad_degree. + RAISE invalid_degree. + ENDIF. + + ev_score = ls_result-score. + ev_rating = ls_result-rating. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0106/faulty/m4_app_scorer.clas.abap b/tasks_gen/eval/G0106/faulty/m4_app_scorer.clas.abap new file mode 100644 index 0000000..eaff958 --- /dev/null +++ b/tasks_gen/eval/G0106/faulty/m4_app_scorer.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}app_scorer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 4. + + TYPES: BEGIN OF ty_result, + status TYPE ty_status, + score TYPE i, + rating TYPE string, + END OF ty_result. + + CONSTANTS: BEGIN OF co_status, + ok TYPE ty_status VALUE 'OK', + bad_years TYPE ty_status VALUE 'YEAR', + bad_match TYPE ty_status VALUE 'MATC', + bad_degree TYPE ty_status VALUE 'DEGR', + END OF co_status. + + METHODS evaluate + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + RETURNING + VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + METHODS experience_points + IMPORTING iv_years TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS education_points + IMPORTING iv_degree TYPE string + RETURNING VALUE(rv_points) TYPE i. + METHODS skill_points + IMPORTING iv_match TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS rating_for + IMPORTING iv_score TYPE i + RETURNING VALUE(rv_rating) TYPE string. +ENDCLASS. + + +CLASS {{p}}app_scorer IMPLEMENTATION. + + METHOD evaluate. + rs_result = VALUE #( status = co_status-ok ). + + IF iv_years < 0. + rs_result-status = co_status-bad_years. + RETURN. + ENDIF. + + IF iv_match < 0 OR iv_match > 100. + rs_result-status = co_status-bad_match. + RETURN. + ENDIF. + + DATA(lv_education) = education_points( iv_degree ). + IF lv_education >= 0. + rs_result-status = co_status-bad_degree. + RETURN. + ENDIF. + + rs_result-score = experience_points( iv_years ) + lv_education + skill_points( iv_match ). + rs_result-rating = rating_for( rs_result-score ). + ENDMETHOD. + + METHOD experience_points. + rv_points = iv_years * 8. + IF rv_points > 40. + rv_points = 40. + ENDIF. + ENDMETHOD. + + METHOD education_points. + CASE iv_degree. + WHEN 'PHD'. + rv_points = 30. + WHEN 'MAS'. + rv_points = 25. + WHEN 'BAC'. + rv_points = 20. + WHEN 'VOC'. + rv_points = 10. + WHEN OTHERS. + rv_points = -1. + ENDCASE. + ENDMETHOD. + + METHOD skill_points. + rv_points = ( iv_match * 30 + 50 ) DIV 100. + ENDMETHOD. + + METHOD rating_for. + IF iv_score >= 75. + rv_rating = 'INVITE'. + ELSEIF iv_score >= 50. + rv_rating = 'REVIEW'. + ELSE. + rv_rating = 'REJECT'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/generation.json b/tasks_gen/eval/G0106/generation.json new file mode 100644 index 0000000..993600b --- /dev/null +++ b/tasks_gen/eval/G0106/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0106", + "pool": "eval", + "object_type": "FUNC", + "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/eval/G0106/hidden/t41_hidden.clas.abap b/tasks_gen/eval/G0106/hidden/t41_hidden.clas.abap new file mode 100644 index 0000000..94c6cc5 --- /dev/null +++ b/tasks_gen/eval/G0106/hidden/t41_hidden.clas.abap @@ -0,0 +1,138 @@ +CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call_score + IMPORTING iv_years TYPE i iv_degree TYPE string iv_match TYPE i + EXPORTING ev_score TYPE i ev_rating TYPE string ev_subrc TYPE sysubrc. + METHODS class_experience_points FOR TESTING. + METHODS class_education_points FOR TESTING. + METHODS class_skill_rounding FOR TESTING. + METHODS class_rating_limits FOR TESTING. + METHODS class_invalid_years FOR TESTING. + METHODS class_invalid_match FOR TESTING. + METHODS class_invalid_degree FOR TESTING. + METHODS func_returns_result FOR TESTING. + METHODS func_raises_years FOR TESTING. + METHODS func_raises_match FOR TESTING. + METHODS func_raises_degree FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_hidden IMPLEMENTATION. + + METHOD call_score. + CLEAR: ev_score, ev_rating. + CALL FUNCTION '{{P}}APPL_SCORE' + EXPORTING + iv_years = iv_years + iv_degree = iv_degree + iv_match = iv_match + IMPORTING + ev_score = ev_score + ev_rating = ev_rating + EXCEPTIONS + invalid_years = 1 + invalid_match = 2 + invalid_degree = 3 + OTHERS = 4. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD class_experience_points. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_two) = lo_scorer->evaluate( iv_years = 2 iv_degree = 'VOC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 26 act = ls_two-score ). + DATA(ls_cap) = lo_scorer->evaluate( iv_years = 9 iv_degree = 'VOC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = ls_cap-score ). + ENDMETHOD. + + METHOD class_education_points. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_phd) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'PHD' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_phd-score ). + DATA(ls_mas) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'MAS' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 25 act = ls_mas-score ). + DATA(ls_bac) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'BAC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = ls_bac-score ). + DATA(ls_voc) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = ls_voc-score ). + ENDMETHOD. + + METHOD class_skill_rounding. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_full) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 100 ). + cl_abap_unit_assert=>assert_equals( exp = 40 act = ls_full-score ). + DATA(ls_half) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 35 ). + cl_abap_unit_assert=>assert_equals( exp = 21 act = ls_half-score ). + DATA(ls_down) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 33 ). + cl_abap_unit_assert=>assert_equals( exp = 20 act = ls_down-score ). + DATA(ls_low) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 1 ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = ls_low-score ). + ENDMETHOD. + + METHOD class_rating_limits. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_invite) = lo_scorer->evaluate( iv_years = 5 iv_degree = 'MAS' iv_match = 33 ). + cl_abap_unit_assert=>assert_equals( exp = 75 act = ls_invite-score ). + cl_abap_unit_assert=>assert_equals( exp = 'INVITE' act = ls_invite-rating ). + DATA(ls_review) = lo_scorer->evaluate( iv_years = 5 iv_degree = 'MAS' iv_match = 30 ). + cl_abap_unit_assert=>assert_equals( exp = 74 act = ls_review-score ). + cl_abap_unit_assert=>assert_equals( exp = 'REVIEW' act = ls_review-rating ). + DATA(ls_edge) = lo_scorer->evaluate( iv_years = 5 iv_degree = 'VOC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 50 act = ls_edge-score ). + cl_abap_unit_assert=>assert_equals( exp = 'REVIEW' act = ls_edge-rating ). + DATA(ls_low) = lo_scorer->evaluate( iv_years = 3 iv_degree = 'MAS' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 49 act = ls_low-score ). + cl_abap_unit_assert=>assert_equals( exp = 'REJECT' act = ls_low-rating ). + ENDMETHOD. + + METHOD class_invalid_years. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( iv_years = -1 iv_degree = 'PHD' iv_match = 100 ). + cl_abap_unit_assert=>assert_equals( exp = 'YEAR' act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-score ). + cl_abap_unit_assert=>assert_initial( act = ls_result-rating ). + ENDMETHOD. + + METHOD class_invalid_match. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_high) = lo_scorer->evaluate( iv_years = 1 iv_degree = 'PHD' iv_match = 101 ). + cl_abap_unit_assert=>assert_equals( exp = 'MATC' act = ls_high-status ). + DATA(ls_low) = lo_scorer->evaluate( iv_years = 1 iv_degree = 'PHD' iv_match = -1 ). + cl_abap_unit_assert=>assert_equals( exp = 'MATC' act = ls_low-status ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_low-score ). + ENDMETHOD. + + METHOD class_invalid_degree. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( iv_years = 1 iv_degree = 'XYZ' iv_match = 50 ). + cl_abap_unit_assert=>assert_equals( exp = 'DEGR' act = ls_result-status ). + cl_abap_unit_assert=>assert_initial( act = ls_result-rating ). + ENDMETHOD. + + METHOD func_returns_result. + call_score( EXPORTING iv_years = 5 iv_degree = 'PHD' iv_match = 100 + IMPORTING ev_score = DATA(lv_score) ev_rating = DATA(lv_rating) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lv_score ). + cl_abap_unit_assert=>assert_equals( exp = 'INVITE' act = lv_rating ). + ENDMETHOD. + + METHOD func_raises_years. + call_score( EXPORTING iv_years = -3 iv_degree = 'PHD' iv_match = 100 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD func_raises_match. + call_score( EXPORTING iv_years = 5 iv_degree = 'PHD' iv_match = 200 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. + + METHOD func_raises_degree. + call_score( EXPORTING iv_years = 5 iv_degree = 'ABC' iv_match = 100 + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/mutation.json b/tasks_gen/eval/G0106/mutation.json new file mode 100644 index 0000000..18ffd67 --- /dev/null +++ b/tasks_gen/eval/G0106/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G0106", + "mutants": [ + { + "object": "{{P}}APP_SCORER", + "mutant": "line 52: OR -> AND (logic)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "CLASS_INVALID_MATCH", + "FUNC_RAISES_MATCH" + ] + }, + { + "object": "{{P}}APPL_SCORE", + "mutant": "line 20: = -> <> (eq)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "FUNC_RAISES_DEGREE", + "FUNC_RAISES_MATCH", + "FUNC_RAISES_YEARS", + "FUNC_RETURNS_RESULT" + ] + }, + { + "object": "{{P}}APP_SCORER", + "mutant": "line 52: > -> <= (rel)", + "status": "killed", + "hidden": "2/11", + "failed_tests": [ + "CLASS_EDUCATION_POINTS", + "CLASS_EXPERIENCE_POINTS", + "CLASS_INVALID_DEGREE", + "CLASS_INVALID_MATCH", + "CLASS_RATING_LIMITS", + "CLASS_SKILL_ROUNDING", + "FUNC_RAISES_DEGREE", + "FUNC_RAISES_MATCH", + "FUNC_RETURNS_RESULT" + ] + }, + { + "object": "{{P}}APPL_SCORE", + "mutant": "line 22: = -> <> (eq)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "FUNC_RAISES_DEGREE", + "FUNC_RAISES_MATCH", + "FUNC_RETURNS_RESULT" + ] + }, + { + "object": "{{P}}APP_SCORER", + "mutant": "line 58: < -> >= (rel)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "CLASS_EDUCATION_POINTS", + "CLASS_EXPERIENCE_POINTS", + "CLASS_INVALID_DEGREE", + "CLASS_RATING_LIMITS", + "CLASS_SKILL_ROUNDING", + "FUNC_RAISES_DEGREE", + "FUNC_RETURNS_RESULT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0106/reference/app_scorer.clas.abap b/tasks_gen/eval/G0106/reference/app_scorer.clas.abap new file mode 100644 index 0000000..f64b32f --- /dev/null +++ b/tasks_gen/eval/G0106/reference/app_scorer.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}app_scorer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_status TYPE c LENGTH 4. + + TYPES: BEGIN OF ty_result, + status TYPE ty_status, + score TYPE i, + rating TYPE string, + END OF ty_result. + + CONSTANTS: BEGIN OF co_status, + ok TYPE ty_status VALUE 'OK', + bad_years TYPE ty_status VALUE 'YEAR', + bad_match TYPE ty_status VALUE 'MATC', + bad_degree TYPE ty_status VALUE 'DEGR', + END OF co_status. + + METHODS evaluate + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + RETURNING + VALUE(rs_result) TYPE ty_result. + + PRIVATE SECTION. + METHODS experience_points + IMPORTING iv_years TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS education_points + IMPORTING iv_degree TYPE string + RETURNING VALUE(rv_points) TYPE i. + METHODS skill_points + IMPORTING iv_match TYPE i + RETURNING VALUE(rv_points) TYPE i. + METHODS rating_for + IMPORTING iv_score TYPE i + RETURNING VALUE(rv_rating) TYPE string. +ENDCLASS. + + +CLASS {{p}}app_scorer IMPLEMENTATION. + + METHOD evaluate. + rs_result = VALUE #( status = co_status-ok ). + + IF iv_years < 0. + rs_result-status = co_status-bad_years. + RETURN. + ENDIF. + + IF iv_match < 0 OR iv_match > 100. + rs_result-status = co_status-bad_match. + RETURN. + ENDIF. + + DATA(lv_education) = education_points( iv_degree ). + IF lv_education < 0. + rs_result-status = co_status-bad_degree. + RETURN. + ENDIF. + + rs_result-score = experience_points( iv_years ) + lv_education + skill_points( iv_match ). + rs_result-rating = rating_for( rs_result-score ). + ENDMETHOD. + + METHOD experience_points. + rv_points = iv_years * 8. + IF rv_points > 40. + rv_points = 40. + ENDIF. + ENDMETHOD. + + METHOD education_points. + CASE iv_degree. + WHEN 'PHD'. + rv_points = 30. + WHEN 'MAS'. + rv_points = 25. + WHEN 'BAC'. + rv_points = 20. + WHEN 'VOC'. + rv_points = 10. + WHEN OTHERS. + rv_points = -1. + ENDCASE. + ENDMETHOD. + + METHOD skill_points. + rv_points = ( iv_match * 30 + 50 ) DIV 100. + ENDMETHOD. + + METHOD rating_for. + IF iv_score >= 75. + rv_rating = 'INVITE'. + ELSEIF iv_score >= 50. + rv_rating = 'REVIEW'. + ELSE. + rv_rating = 'REJECT'. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/reference/app_scorer.clas.testclasses.abap b/tasks_gen/eval/G0106/reference/app_scorer.clas.testclasses.abap new file mode 100644 index 0000000..5061109 --- /dev/null +++ b/tasks_gen/eval/G0106/reference/app_scorer.clas.testclasses.abap @@ -0,0 +1,33 @@ +CLASS ltc_scorer DEFINITION FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS high_score FOR TESTING. + METHODS low_score FOR TESTING. + METHODS invalid_years FOR TESTING. +ENDCLASS. + + +CLASS ltc_scorer IMPLEMENTATION. + + METHOD high_score. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( iv_years = 5 iv_degree = 'PHD' iv_match = 100 ). + cl_abap_unit_assert=>assert_equals( exp = 'OK' act = ls_result-status ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = 'INVITE' act = ls_result-rating ). + ENDMETHOD. + + METHOD low_score. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( iv_years = 0 iv_degree = 'VOC' iv_match = 0 ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = ls_result-score ). + cl_abap_unit_assert=>assert_equals( exp = 'REJECT' act = ls_result-rating ). + ENDMETHOD. + + METHOD invalid_years. + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( iv_years = -1 iv_degree = 'PHD' iv_match = 100 ). + cl_abap_unit_assert=>assert_equals( exp = 'YEAR' act = ls_result-status ). + cl_abap_unit_assert=>assert_initial( act = ls_result-rating ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/reference/appl_score.func.abap b/tasks_gen/eval/G0106/reference/appl_score.func.abap new file mode 100644 index 0000000..87b01be --- /dev/null +++ b/tasks_gen/eval/G0106/reference/appl_score.func.abap @@ -0,0 +1,31 @@ +FUNCTION {{p}}appl_score + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + EXPORTING + ev_score TYPE i + ev_rating TYPE string + EXCEPTIONS + invalid_years + invalid_match + invalid_degree. + + DATA(lo_scorer) = NEW {{p}}app_scorer( ). + DATA(ls_result) = lo_scorer->evaluate( + iv_years = iv_years + iv_degree = iv_degree + iv_match = iv_match ). + + IF ls_result-status = {{p}}app_scorer=>co_status-bad_years. + RAISE invalid_years. + ELSEIF ls_result-status = {{p}}app_scorer=>co_status-bad_match. + RAISE invalid_match. + ELSEIF ls_result-status = {{p}}app_scorer=>co_status-bad_degree. + RAISE invalid_degree. + ENDIF. + + ev_score = ls_result-score. + ev_rating = ls_result-rating. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0106/reference/t41_test.clas.abap b/tasks_gen/eval/G0106/reference/t41_test.clas.abap new file mode 100644 index 0000000..5f15ad3 --- /dev/null +++ b/tasks_gen/eval/G0106/reference/t41_test.clas.abap @@ -0,0 +1,61 @@ +CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS score_of + IMPORTING iv_years TYPE i iv_degree TYPE string iv_match TYPE i + RETURNING VALUE(rv_score) TYPE i. + METHODS invite_for_expert FOR TESTING. + METHODS review_for_graduate FOR TESTING. + METHODS reject_for_junior FOR TESTING. + METHODS invalid_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t41_test IMPLEMENTATION. + + METHOD score_of. + CALL FUNCTION '{{P}}APPL_SCORE' + EXPORTING + iv_years = iv_years + iv_degree = iv_degree + iv_match = iv_match + IMPORTING + ev_score = rv_score + EXCEPTIONS + invalid_years = 1 + invalid_match = 2 + invalid_degree = 3 + OTHERS = 4. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD invite_for_expert. + cl_abap_unit_assert=>assert_equals( + exp = 100 + act = score_of( iv_years = 5 iv_degree = 'PHD' iv_match = 100 ) ). + ENDMETHOD. + + METHOD review_for_graduate. + cl_abap_unit_assert=>assert_equals( + exp = 50 + act = score_of( iv_years = 5 iv_degree = 'VOC' iv_match = 0 ) ). + ENDMETHOD. + + METHOD reject_for_junior. + cl_abap_unit_assert=>assert_equals( + exp = 18 + act = score_of( iv_years = 1 iv_degree = 'VOC' iv_match = 0 ) ). + ENDMETHOD. + + METHOD invalid_input. + CALL FUNCTION '{{P}}APPL_SCORE' + EXPORTING + iv_years = -1 + iv_degree = 'PHD' + iv_match = 50 + EXCEPTIONS + invalid_years = 1 + OTHERS = 2. + cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0106/seed/appl_sample.func.abap b/tasks_gen/eval/G0106/seed/appl_sample.func.abap new file mode 100644 index 0000000..eda0968 --- /dev/null +++ b/tasks_gen/eval/G0106/seed/appl_sample.func.abap @@ -0,0 +1,15 @@ +FUNCTION {{p}}appl_sample + IMPORTING + iv_years TYPE i + EXPORTING + ev_note TYPE string + EXCEPTIONS + invalid_years. + + IF iv_years < 0. + RAISE invalid_years. + ENDIF. + + ev_note = |{ iv_years } years of experience|. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0106/spec.md b/tasks_gen/eval/G0106/spec.md new file mode 100644 index 0000000..6e9daa7 --- /dev/null +++ b/tasks_gen/eval/G0106/spec.md @@ -0,0 +1,105 @@ +# 1. Goal +A recruitment agency scores job applicants. A recruitment program calls one +function module for each applicant. The function module returns a score and a +recommendation. + +The score calculation is pure logic. Put this logic in a new global class. The +function module calls this class. Do not put the calculation in the function +module. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_HR exists in package $TMP. +- Function module {{P}}APPL_SAMPLE exists in this group. It shows the + parameter style of the group. +- Create all new objects in package $TMP. + +# 4. Contract + +## 4.1 Function module {{P}}APPL_SCORE +Create the function module {{P}}APPL_SCORE in function group {{P}}FG_HR. +Use this interface: + +``` +FUNCTION {{p}}appl_score + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + EXPORTING + ev_score TYPE i + ev_rating TYPE string + EXCEPTIONS + invalid_years + invalid_match + invalid_degree. +``` + +## 4.2 Class {{P}}APP_SCORER +Create the class {{P}}APP_SCORER. The class is public and final. The class has +a public constructor without parameters. + +Public type TY_RESULT has these components: + +| Component | Type | Meaning | +|-----------|-----------|---------------------------| +| STATUS | TY_STATUS | Result of the input check | +| SCORE | i | Score from 0 to 100 | +| RATING | string | Recommendation | + +TY_STATUS is a character type of length 4. The values of STATUS are 'OK', +'YEAR', 'MATC' and 'DEGR'. + +Public method EVALUATE: + +``` +METHODS evaluate + IMPORTING + iv_years TYPE i + iv_degree TYPE string + iv_match TYPE i + RETURNING + VALUE(rs_result) TYPE ty_result. +``` + +The method does not raise an exception. + +# 5. Business rules +1. Input check. Use this order: + 1. If IV_YEARS is less than 0, STATUS is 'YEAR'. + 2. Else if IV_MATCH is less than 0 or greater than 100, STATUS is 'MATC'. + 3. Else if IV_DEGREE is not 'PHD', 'MAS', 'BAC' or 'VOC', STATUS is 'DEGR'. + 4. Else STATUS is 'OK'. +2. If STATUS is not 'OK', SCORE is 0 and RATING is empty. +3. Experience points: 8 points for each full year of experience. The maximum + is 40 points. +4. Education points: 'PHD' gives 30 points, 'MAS' 25 points, 'BAC' 20 points + and 'VOC' 10 points. +5. Skill points: IV_MATCH percent of 30 points. Round the result half up to a + whole number. +6. SCORE is the sum of the experience points, the education points and the + skill points. +7. RATING depends on SCORE: + - SCORE 75 or more: 'INVITE' + - SCORE 50 to 74: 'REVIEW' + - SCORE less than 50: 'REJECT' +8. The function module raises the exception that belongs to STATUS: + - 'YEAR' raises INVALID_YEARS + - 'MATC' raises INVALID_MATCH + - 'DEGR' raises INVALID_DEGREE + For STATUS 'OK' the function module raises no exception. It exports SCORE + and RATING. + +# 6. Constraints +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Coding standard: Clean ABAP. Each method has less than 40 statements. +- No ATC finding of priority 1 or 2. +- Out of scope: do not change {{P}}APPL_SAMPLE. + +# 7. Acceptance +- {{P}}APPL_SCORE and {{P}}APP_SCORER are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the class in local test classes of the class. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/eval/G0106/task.json b/tasks_gen/eval/G0106/task.json new file mode 100644 index 0000000..c12ea94 --- /dev/null +++ b/tasks_gen/eval/G0106/task.json @@ -0,0 +1,95 @@ +{ + "id": "G0106", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_HR", + "description": "Recruitment function group" + }, + { + "type": "FUNC", + "name": "{{P}}APPL_SAMPLE", + "functionGroup": "{{P}}FG_HR", + "file": "seed/appl_sample.func.abap", + "description": "Sample applicant module" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}APPL_SCORE", + "functionGroup": "{{P}}FG_HR", + "params": [ + { + "name": "iv_years", + "type": "i" + }, + { + "name": "iv_degree", + "type": "string" + }, + { + "name": "iv_match", + "type": "i" + }, + { + "name": "ev_score", + "type": "i" + }, + { + "name": "ev_rating", + "type": "string" + } + ] + }, + { + "type": "CLAS", + "name": "{{P}}APP_SCORER" + } + ], + "out_of_scope": [ + "{{P}}APPL_SAMPLE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T41_HIDDEN", + "file": "hidden/t41_hidden.clas.abap", + "description": "T41 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}APP_SCORER", + "file": "reference/app_scorer.clas.abap", + "testclasses_file": "reference/app_scorer.clas.testclasses.abap", + "description": "Applicant scoring logic" + }, + { + "type": "FUNC", + "name": "{{P}}APPL_SCORE", + "functionGroup": "{{P}}FG_HR", + "file": "reference/appl_score.func.abap", + "description": "Applicant score module" + }, + { + "type": "CLAS", + "name": "{{P}}T41_TEST", + "file": "reference/t41_test.clas.abap", + "description": "T41 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0107/faulty/m0_energy_bill.func.abap b/tasks_gen/eval/G0107/faulty/m0_energy_bill.func.abap new file mode 100644 index 0000000..3cb56e7 --- /dev/null +++ b/tasks_gen/eval/G0107/faulty/m0_energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption >= 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty * lv_rate + + lv_reduced_qty * lv_rate * lc_tier_factor. + IF iv_direct_debit = abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 2 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/faulty/m1_energy_bill.func.abap b/tasks_gen/eval/G0107/faulty/m1_energy_bill.func.abap new file mode 100644 index 0000000..4961898 --- /dev/null +++ b/tasks_gen/eval/G0107/faulty/m1_energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption < 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty / lv_rate + + lv_reduced_qty * lv_rate * lc_tier_factor. + IF iv_direct_debit = abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 2 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/faulty/m2_energy_bill.func.abap b/tasks_gen/eval/G0107/faulty/m2_energy_bill.func.abap new file mode 100644 index 0000000..e3cb709 --- /dev/null +++ b/tasks_gen/eval/G0107/faulty/m2_energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption < 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty * lv_rate + + lv_reduced_qty * lv_rate / lc_tier_factor. + IF iv_direct_debit = abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 2 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/faulty/m3_energy_bill.func.abap b/tasks_gen/eval/G0107/faulty/m3_energy_bill.func.abap new file mode 100644 index 0000000..16a7195 --- /dev/null +++ b/tasks_gen/eval/G0107/faulty/m3_energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption < 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty * lv_rate + + lv_reduced_qty * lv_rate * lc_tier_factor. + IF iv_direct_debit <> abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 2 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/faulty/m4_energy_bill.func.abap b/tasks_gen/eval/G0107/faulty/m4_energy_bill.func.abap new file mode 100644 index 0000000..33a6a8c --- /dev/null +++ b/tasks_gen/eval/G0107/faulty/m4_energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption < 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty * lv_rate + + lv_reduced_qty * lv_rate * lc_tier_factor. + IF iv_direct_debit = abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 3 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/generation.json b/tasks_gen/eval/G0107/generation.json new file mode 100644 index 0000000..1d56c19 --- /dev/null +++ b/tasks_gen/eval/G0107/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0107", + "pool": "eval", + "object_type": "FUNC", + "category": "A", + "attempts": [ + { + "stage": "validate", + "oracle": 85.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/eval/G0107/hidden/t21_hidden.clas.abap b/tasks_gen/eval/G0107/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..18772df --- /dev/null +++ b/tasks_gen/eval/G0107/hidden/t21_hidden.clas.abap @@ -0,0 +1,100 @@ +CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool DEFAULT abap_false + EXPORTING + ev_bill TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS basic_no_tier FOR TESTING. + METHODS tier_discount FOR TESTING. + METHODS green_tariff FOR TESTING. + METHODS night_tariff FOR TESTING. + METHODS direct_debit FOR TESTING. + METHODS rounding FOR TESTING. + METHODS tier_boundary FOR TESTING. + METHODS zero_consumption FOR TESTING. + METHODS negative_consumption FOR TESTING. + METHODS unknown_tariff FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD bill. + CALL FUNCTION '{{P}}ENERGY_BILL' + EXPORTING + iv_consumption = iv_consumption + iv_tariff = iv_tariff + iv_direct_debit = iv_direct_debit + IMPORTING + ev_bill = ev_bill + EXCEPTIONS + invalid_consumption = 1 + invalid_tariff = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD basic_no_tier. + bill( EXPORTING iv_consumption = 100 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '44.00' ) act = lv_bill ). + ENDMETHOD. + + METHOD tier_discount. + bill( EXPORTING iv_consumption = 400 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '133.60' ) act = lv_bill ). + ENDMETHOD. + + METHOD green_tariff. + bill( EXPORTING iv_consumption = 250 iv_tariff = `GREEN` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '107.00' ) act = lv_bill ). + ENDMETHOD. + + METHOD night_tariff. + bill( EXPORTING iv_consumption = 500 iv_tariff = `NIGHT` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '122.40' ) act = lv_bill ). + ENDMETHOD. + + METHOD direct_debit. + bill( EXPORTING iv_consumption = 400 iv_tariff = `BASIC` iv_direct_debit = abap_true + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '130.93' ) act = lv_bill ). + ENDMETHOD. + + METHOD rounding. + bill( EXPORTING iv_consumption = 309 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '110.30' ) act = lv_bill ). + ENDMETHOD. + + METHOD tier_boundary. + bill( EXPORTING iv_consumption = 300 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '108.00' ) act = lv_bill ). + ENDMETHOD. + + METHOD zero_consumption. + bill( EXPORTING iv_consumption = 0 iv_tariff = `NIGHT` + IMPORTING ev_bill = DATA(lv_bill) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '12.00' ) act = lv_bill ). + ENDMETHOD. + + METHOD negative_consumption. + bill( EXPORTING iv_consumption = -1 iv_tariff = `BASIC` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD unknown_tariff. + bill( EXPORTING iv_consumption = 100 iv_tariff = `ECO` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0107/mutation.json b/tasks_gen/eval/G0107/mutation.json new file mode 100644 index 0000000..d30ad09 --- /dev/null +++ b/tasks_gen/eval/G0107/mutation.json @@ -0,0 +1,80 @@ +{ + "task": "G0107", + "mutants": [ + { + "object": "{{P}}ENERGY_BILL", + "mutant": "line 17: < -> >= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "BASIC_NO_TIER", + "DIRECT_DEBIT", + "GREEN_TARIFF", + "NEGATIVE_CONSUMPTION", + "NIGHT_TARIFF", + "ROUNDING", + "TIER_BOUNDARY", + "TIER_DISCOUNT", + "UNKNOWN_TARIFF", + "ZERO_CONSUMPTION" + ] + }, + { + "object": "{{P}}ENERGY_BILL", + "mutant": "line 44: * -> / (arith)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "BASIC_NO_TIER", + "DIRECT_DEBIT", + "GREEN_TARIFF", + "NIGHT_TARIFF", + "ROUNDING", + "TIER_BOUNDARY", + "TIER_DISCOUNT" + ] + }, + { + "object": "{{P}}ENERGY_BILL", + "mutant": "line 45: * -> / (arith)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "DIRECT_DEBIT", + "NIGHT_TARIFF", + "ROUNDING", + "TIER_DISCOUNT" + ] + }, + { + "object": "{{P}}ENERGY_BILL", + "mutant": "line 46: = -> <> (eq)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "BASIC_NO_TIER", + "DIRECT_DEBIT", + "GREEN_TARIFF", + "NIGHT_TARIFF", + "ROUNDING", + "TIER_BOUNDARY", + "TIER_DISCOUNT", + "ZERO_CONSUMPTION" + ] + }, + { + "object": "{{P}}ENERGY_BILL", + "mutant": "line 50: 2 -> 3 (const)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "DIRECT_DEBIT", + "ROUNDING" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0107/reference/energy_bill.func.abap b/tasks_gen/eval/G0107/reference/energy_bill.func.abap new file mode 100644 index 0000000..58cc43a --- /dev/null +++ b/tasks_gen/eval/G0107/reference/energy_bill.func.abap @@ -0,0 +1,51 @@ +FUNCTION {{p}}energy_bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool + EXPORTING + ev_bill TYPE decfloat34 + EXCEPTIONS + invalid_consumption + invalid_tariff. + + CONSTANTS lc_base_fee TYPE decfloat34 VALUE '12.00'. + CONSTANTS lc_tier_limit TYPE decfloat34 VALUE '300'. + CONSTANTS lc_tier_factor TYPE decfloat34 VALUE '0.8'. + CONSTANTS lc_debit_factor TYPE decfloat34 VALUE '0.98'. + + IF iv_consumption < 0. + RAISE invalid_consumption. + ENDIF. + + DATA lv_rate TYPE decfloat34. + CASE iv_tariff. + WHEN 'BASIC'. + lv_rate = '0.32'. + WHEN 'GREEN'. + lv_rate = '0.38'. + WHEN 'NIGHT'. + lv_rate = '0.24'. + WHEN OTHERS. + RAISE invalid_tariff. + ENDCASE. + + DATA lv_standard_qty TYPE decfloat34. + DATA lv_reduced_qty TYPE decfloat34. + IF iv_consumption > lc_tier_limit. + lv_standard_qty = lc_tier_limit. + lv_reduced_qty = iv_consumption - lc_tier_limit. + ELSE. + lv_standard_qty = iv_consumption. + lv_reduced_qty = 0. + ENDIF. + + DATA lv_bill TYPE decfloat34. + lv_bill = lc_base_fee + lv_standard_qty * lv_rate + + lv_reduced_qty * lv_rate * lc_tier_factor. + IF iv_direct_debit = abap_true. + lv_bill = lv_bill * lc_debit_factor. + ENDIF. + + ev_bill = round( val = lv_bill dec = 2 mode = cl_abap_math=>round_half_up ). +ENDFUNCTION. diff --git a/tasks_gen/eval/G0107/reference/t21_test.clas.abap b/tasks_gen/eval/G0107/reference/t21_test.clas.abap new file mode 100644 index 0000000..8c19813 --- /dev/null +++ b/tasks_gen/eval/G0107/reference/t21_test.clas.abap @@ -0,0 +1,68 @@ +CLASS {{p}}t21_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS bill + IMPORTING + iv_consumption TYPE decfloat34 + iv_tariff TYPE string + iv_direct_debit TYPE abap_bool DEFAULT abap_false + EXPORTING + ev_bill TYPE decfloat34 + ev_subrc TYPE sysubrc. + METHODS basic_bill FOR TESTING. + METHODS tier_bill FOR TESTING. + METHODS debit_bill FOR TESTING. + METHODS invalid_input FOR TESTING. + METHODS unknown_tariff FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_test IMPLEMENTATION. + METHOD bill. + CALL FUNCTION '{{P}}ENERGY_BILL' + EXPORTING + iv_consumption = iv_consumption + iv_tariff = iv_tariff + iv_direct_debit = iv_direct_debit + IMPORTING + ev_bill = ev_bill + EXCEPTIONS + invalid_consumption = 1 + invalid_tariff = 2 + OTHERS = 3. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD basic_bill. + bill( EXPORTING iv_consumption = 100 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '44.00' ) act = lv_bill ). + ENDMETHOD. + + METHOD tier_bill. + bill( EXPORTING iv_consumption = 400 iv_tariff = `BASIC` + IMPORTING ev_bill = DATA(lv_bill) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '133.60' ) act = lv_bill ). + ENDMETHOD. + + METHOD debit_bill. + bill( EXPORTING iv_consumption = 400 iv_tariff = `BASIC` iv_direct_debit = abap_true + IMPORTING ev_bill = DATA(lv_bill) ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '130.93' ) act = lv_bill ). + ENDMETHOD. + + METHOD invalid_input. + bill( EXPORTING iv_consumption = -1 iv_tariff = `BASIC` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ). + ENDMETHOD. + + METHOD unknown_tariff. + bill( EXPORTING iv_consumption = 100 iv_tariff = `ECO` + IMPORTING ev_subrc = DATA(lv_rc) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0107/spec.md b/tasks_gen/eval/G0107/spec.md new file mode 100644 index 0000000..e2f8099 --- /dev/null +++ b/tasks_gen/eval/G0107/spec.md @@ -0,0 +1,61 @@ +# 1. Goal + +An energy supplier bills households for electricity. The billing program calls +one function module for each household. The function module calculates the bill +of one month. + +# 2. Open questions + +None. + +# 3. Context + +- The function group {{P}}FG_BILL exists in package $TMP. The group is empty. +- The billing program calls the function module once for each household. It + passes the consumption of the month, the tariff, and the payment method. +- The consumption is a quantity in kilowatt hours (kWh). +- The bill is an amount in euro. + +# 4. Contract + +- Create the function module {{P}}ENERGY_BILL in function group {{P}}FG_BILL. +- Use this interface: + +| Parameter | Kind | Type | Meaning | +| --- | --- | --- | --- | +| iv_consumption | Importing | decfloat34 | Consumption of the month in kWh | +| iv_tariff | Importing | string | Tariff code | +| iv_direct_debit | Importing | abap_bool | abap_true if the customer pays by direct debit | +| ev_bill | Exporting | decfloat34 | Bill in euro | +| invalid_consumption | Exception | | See rule 1 | +| invalid_tariff | Exception | | See rule 2 | + +# 5. Business rules + +1. If the consumption is less than zero, raise the exception + INVALID_CONSUMPTION. Do not set the bill. +2. The valid tariff codes are BASIC, GREEN, and NIGHT. Any other tariff code + raises the exception INVALID_TARIFF. Do not set the bill. +3. Every bill contains a base fee of 12.00 euro. +4. The working price for one kWh is: + - BASIC: 0.32 euro + - GREEN: 0.38 euro + - NIGHT: 0.24 euro +5. For a consumption above 300 kWh, the working price of the part above 300 kWh + is 80 % of the working price of the tariff. +6. If the customer pays by direct debit, the bill is 2 % lower. +7. Round the bill half up to 2 decimals. + +# 6. Constraints + +- Release target: ABAP Platform 2025 (SAP_BASIS 816). +- Package: $TMP. Do not use transports. +- Coding standard: Clean ABAP. Keep every method below 40 statements. +- Out of scope: database access, CDS views, reports, ALV, dynpro, and changes + to other objects. + +# 7. Acceptance + +- The function module {{P}}ENERGY_BILL is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests for the function module in a global test class. diff --git a/tasks_gen/eval/G0107/task.json b/tasks_gen/eval/G0107/task.json new file mode 100644 index 0000000..9a78cbb --- /dev/null +++ b/tasks_gen/eval/G0107/task.json @@ -0,0 +1,71 @@ +{ + "id": "G0107", + "category": "A", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_BILL", + "description": "Billing function group (empty)" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}ENERGY_BILL", + "functionGroup": "{{P}}FG_BILL", + "params": [ + { + "name": "iv_consumption", + "type": "decfloat34" + }, + { + "name": "iv_tariff", + "type": "string" + }, + { + "name": "iv_direct_debit", + "type": "abap_bool" + }, + { + "name": "ev_bill", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}ENERGY_BILL", + "functionGroup": "{{P}}FG_BILL", + "file": "reference/energy_bill.func.abap", + "description": "Electricity bill module" + }, + { + "type": "CLAS", + "name": "{{P}}T21_TEST", + "file": "reference/t21_test.clas.abap", + "description": "T21 own tests" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0108/faulty/m0_i_call_charge.ddls.asddls b/tasks_gen/eval/G0108/faulty/m0_i_call_charge.ddls.asddls new file mode 100644 index 0000000..bbac220 --- /dev/null +++ b/tasks_gen/eval/G0108/faulty/m0_i_call_charge.ddls.asddls @@ -0,0 +1,32 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Call charge' +define view entity {{P}}I_CALL_CHARGE + as select from {{p}}calls +{ + key call_id as call_id, + customer as customer, + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min, + cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 16 + end as abap.dec(5,2) + ) / 100 as rate, + cast( + round( + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) ) + * ( cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 ) + * case when duration_sec > 1800 + then cast( 90 as abap.dec(5,2) ) / 100 + else cast( 100 as abap.dec(5,2) ) / 100 + end + , 2 ) + as abap.dec(15,2) ) as charge +} diff --git a/tasks_gen/eval/G0108/faulty/m1_i_call_charge.ddls.asddls b/tasks_gen/eval/G0108/faulty/m1_i_call_charge.ddls.asddls new file mode 100644 index 0000000..7c68536 --- /dev/null +++ b/tasks_gen/eval/G0108/faulty/m1_i_call_charge.ddls.asddls @@ -0,0 +1,32 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Call charge' +define view entity {{P}}I_CALL_CHARGE + as select from {{p}}calls +{ + key call_id as call_id, + customer as customer, + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min, + cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 as rate, + cast( + round( + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) ) + * ( cast( + case dest_type + when 'N' then 9 + when 'Z' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 ) + * case when duration_sec > 1800 + then cast( 90 as abap.dec(5,2) ) / 100 + else cast( 100 as abap.dec(5,2) ) / 100 + end + , 2 ) + as abap.dec(15,2) ) as charge +} diff --git a/tasks_gen/eval/G0108/faulty/m2_i_call_charge.ddls.asddls b/tasks_gen/eval/G0108/faulty/m2_i_call_charge.ddls.asddls new file mode 100644 index 0000000..aed4079 --- /dev/null +++ b/tasks_gen/eval/G0108/faulty/m2_i_call_charge.ddls.asddls @@ -0,0 +1,32 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Call charge' +define view entity {{P}}I_CALL_CHARGE + as select from {{p}}calls +{ + key call_id as call_id, + customer as customer, + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min, + cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 as rate, + cast( + round( + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) ) + * ( cast( + case dest_type + when 'N' then 9 + when 'I' then 26 + else 15 + end as abap.dec(5,2) + ) / 100 ) + * case when duration_sec > 1800 + then cast( 90 as abap.dec(5,2) ) / 100 + else cast( 100 as abap.dec(5,2) ) / 100 + end + , 2 ) + as abap.dec(15,2) ) as charge +} diff --git a/tasks_gen/eval/G0108/faulty/m4_i_call_charge.ddls.asddls b/tasks_gen/eval/G0108/faulty/m4_i_call_charge.ddls.asddls new file mode 100644 index 0000000..3e6a98f --- /dev/null +++ b/tasks_gen/eval/G0108/faulty/m4_i_call_charge.ddls.asddls @@ -0,0 +1,32 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Call charge' +define view entity {{P}}I_CALL_CHARGE + as select from {{p}}calls +{ + key call_id as call_id, + customer as customer, + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min, + cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 as rate, + cast( + round( + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) ) + * ( cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 ) + * case when duration_sec <= 1800 + then cast( 90 as abap.dec(5,2) ) / 100 + else cast( 100 as abap.dec(5,2) ) / 100 + end + , 2 ) + as abap.dec(15,2) ) as charge +} diff --git a/tasks_gen/eval/G0108/generation.json b/tasks_gen/eval/G0108/generation.json new file mode 100644 index 0000000..2bb95d2 --- /dev/null +++ b/tasks_gen/eval/G0108/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G0108", + "pool": "eval", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 65.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 65.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 98.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/eval/G0108/hidden/t16_hidden.clas.abap b/tasks_gen/eval/G0108/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..7f24530 --- /dev/null +++ b/tasks_gen/eval/G0108/hidden/t16_hidden.clas.abap @@ -0,0 +1,110 @@ +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 assert_amount + IMPORTING iv_exp TYPE decfloat34 + iv_act TYPE decfloat34. + METHODS billable_min_rounds_up FOR TESTING. + METHODS billable_min_exact FOR TESTING. + METHODS rate_national FOR TESTING. + METHODS rate_international FOR TESTING. + METHODS rate_other FOR TESTING. + METHODS charge_no_discount FOR TESTING. + METHODS charge_long_discount FOR TESTING. + METHODS charge_rounding FOR TESTING. + METHODS charge_boundary FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CALL_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_calls TYPE STANDARD TABLE OF {{p}}calls WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_calls = VALUE #( + ( call_id = 'C1' customer = 'ACME' call_date = '20240110' start_time = '100000' duration_sec = 125 dest_type = 'N' ) + ( call_id = 'C2' customer = 'BETA' call_date = '20240110' start_time = '220000' duration_sec = 3600 dest_type = 'I' ) + ( call_id = 'C3' customer = 'GAMMA' call_date = '20240110' start_time = '073000' duration_sec = 30 dest_type = 'N' ) + ( call_id = 'C4' customer = 'DELTA' call_date = '20240110' start_time = '120000' duration_sec = 120 dest_type = 'X' ) + ( call_id = 'C5' customer = 'EPSILON' call_date = '20240110' start_time = '120000' duration_sec = 1800 dest_type = 'N' ) + ( call_id = 'C6' customer = 'ZETA' call_date = '20240110' start_time = '120000' duration_sec = 1801 dest_type = 'N' ) + ). + go_env->insert_test_data( lt_calls ). + ENDMETHOD. + + METHOD assert_amount. + DATA lv_diff TYPE decfloat34. + lv_diff = abs( iv_exp - iv_act ). + cl_abap_unit_assert=>assert_equals( + exp = abap_true + act = xsdbool( lv_diff < CONV decfloat34( '0.005' ) ) ). + ENDMETHOD. + + METHOD billable_min_rounds_up. + SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_min1). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_min1 ). + SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_min2). + cl_abap_unit_assert=>assert_equals( exp = 60 act = lv_min2 ). + SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C3' INTO @DATA(lv_min3). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min3 ). + SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C4' INTO @DATA(lv_min4). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_min4 ). + ENDMETHOD. + + METHOD billable_min_exact. + SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C5' INTO @DATA(lv_min). + cl_abap_unit_assert=>assert_equals( exp = 30 act = lv_min ). + ENDMETHOD. + + METHOD rate_national. + SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_rate). + assert_amount( iv_exp = CONV decfloat34( '0.09' ) iv_act = CONV decfloat34( lv_rate ) ). + ENDMETHOD. + + METHOD rate_international. + SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_rate). + assert_amount( iv_exp = CONV decfloat34( '0.25' ) iv_act = CONV decfloat34( lv_rate ) ). + ENDMETHOD. + + METHOD rate_other. + SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C4' INTO @DATA(lv_rate). + assert_amount( iv_exp = CONV decfloat34( '0.15' ) iv_act = CONV decfloat34( lv_rate ) ). + ENDMETHOD. + + METHOD charge_no_discount. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '0.27' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_long_discount. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '13.50' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_rounding. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C6' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '2.51' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_boundary. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C5' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '2.70' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_call_charge INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0108/mutation.json b/tasks_gen/eval/G0108/mutation.json new file mode 100644 index 0000000..876b409 --- /dev/null +++ b/tasks_gen/eval/G0108/mutation.json @@ -0,0 +1,55 @@ +{ + "task": "G0108", + "mutants": [ + { + "object": "{{P}}I_CALL_CHARGE", + "mutant": "line 13: 15 -> 16 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "RATE_OTHER" + ] + }, + { + "object": "{{P}}I_CALL_CHARGE", + "mutant": "line 22: 'I' -> 'Z' (lit)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "CHARGE_LONG_DISCOUNT" + ] + }, + { + "object": "{{P}}I_CALL_CHARGE", + "mutant": "line 22: 25 -> 26 (const)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "CHARGE_LONG_DISCOUNT" + ] + }, + { + "object": "{{P}}I_CALL_CHARGE", + "mutant": "line 23: 15 -> 16 (const)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + }, + { + "object": "{{P}}I_CALL_CHARGE", + "mutant": "line 26: > -> <= (rel)", + "status": "killed", + "hidden": "6/10", + "failed_tests": [ + "CHARGE_BOUNDARY", + "CHARGE_LONG_DISCOUNT", + "CHARGE_NO_DISCOUNT", + "CHARGE_ROUNDING" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0108/reference/i_call_charge.ddls.asddls b/tasks_gen/eval/G0108/reference/i_call_charge.ddls.asddls new file mode 100644 index 0000000..201c046 --- /dev/null +++ b/tasks_gen/eval/G0108/reference/i_call_charge.ddls.asddls @@ -0,0 +1,32 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Call charge' +define view entity {{P}}I_CALL_CHARGE + as select from {{p}}calls +{ + key call_id as call_id, + customer as customer, + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min, + cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 as rate, + cast( + round( + cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) ) + * ( cast( + case dest_type + when 'N' then 9 + when 'I' then 25 + else 15 + end as abap.dec(5,2) + ) / 100 ) + * case when duration_sec > 1800 + then cast( 90 as abap.dec(5,2) ) / 100 + else cast( 100 as abap.dec(5,2) ) / 100 + end + , 2 ) + as abap.dec(15,2) ) as charge +} diff --git a/tasks_gen/eval/G0108/reference/t16_test.clas.abap b/tasks_gen/eval/G0108/reference/t16_test.clas.abap new file mode 100644 index 0000000..57238fa --- /dev/null +++ b/tasks_gen/eval/G0108/reference/t16_test.clas.abap @@ -0,0 +1,52 @@ +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 assert_amount + IMPORTING iv_exp TYPE decfloat34 + iv_act TYPE decfloat34. + METHODS charge_of_national_call FOR TESTING. + METHODS charge_of_long_call FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CALL_CHARGE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_calls TYPE STANDARD TABLE OF {{p}}calls WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_calls = VALUE #( + ( call_id = 'A' customer = 'C1' call_date = '20240110' start_time = '100000' duration_sec = 60 dest_type = 'N' ) + ( call_id = 'B' customer = 'C2' call_date = '20240110' start_time = '100000' duration_sec = 3600 dest_type = 'I' ) + ). + go_env->insert_test_data( lt_calls ). + ENDMETHOD. + + METHOD assert_amount. + DATA lv_diff TYPE decfloat34. + lv_diff = abs( iv_exp - iv_act ). + cl_abap_unit_assert=>assert_equals( + exp = abap_true + act = xsdbool( lv_diff < CONV decfloat34( '0.005' ) ) ). + ENDMETHOD. + + METHOD charge_of_national_call. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'A' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '0.09' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. + + METHOD charge_of_long_call. + SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'B' INTO @DATA(lv_charge). + assert_amount( iv_exp = CONV decfloat34( '13.50' ) iv_act = CONV decfloat34( lv_charge ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0108/seed/calls.tabl.asabap b/tasks_gen/eval/G0108/seed/calls.tabl.asabap new file mode 100644 index 0000000..8adfea2 --- /dev/null +++ b/tasks_gen/eval/G0108/seed/calls.tabl.asabap @@ -0,0 +1,14 @@ +@EndUserText.label : 'Phone call' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}calls { + key client : abap.clnt not null; + key call_id : abap.char(10) not null; + customer : abap.char(10); + call_date : abap.dats; + start_time : abap.tims; + duration_sec : abap.int4; + dest_type : abap.char(1); +} diff --git a/tasks_gen/eval/G0108/spec.md b/tasks_gen/eval/G0108/spec.md new file mode 100644 index 0000000..b189f38 --- /dev/null +++ b/tasks_gen/eval/G0108/spec.md @@ -0,0 +1,37 @@ +# 1. Goal +Give the charge of each phone call. A billing program will read the charges, so the +calculation must be in the database, in a CDS view. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}CALLS (phone calls), package $TMP. Key: CALL_ID (CHAR 10). + Fields: CUSTOMER (CHAR 10), CALL_DATE (DATS), START_TIME (TIMS), DURATION_SEC (INT4), + DEST_TYPE (CHAR 1). + +# 4. Contract +- Create the CDS view entity {{P}}I_CALL_CHARGE in package $TMP. +- Elements, with these names: CALL_ID (key), CUSTOMER, BILLABLE_MIN, RATE, CHARGE. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each call. +2. BILLABLE_MIN is the duration in minutes, rounded up to the next full minute. A call + of 1 second counts as 1 minute. +3. RATE is the price per minute for the destination type: + - 'N' (national): 0.09 + - 'I' (international): 0.25 + - any other value: 0.15 +4. Calls longer than 1800 seconds get a 10% discount on the charge. +5. CHARGE is BILLABLE_MIN multiplied by RATE, with the discount applied, rounded to 2 + decimals. + +# 6. Constraints +- Release target: 8.16. +- Out of scope: do not change {{P}}CALLS. + +# 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/eval/G0108/task.json b/tasks_gen/eval/G0108/task.json new file mode 100644 index 0000000..90c9877 --- /dev/null +++ b/tasks_gen/eval/G0108/task.json @@ -0,0 +1,59 @@ +{ + "id": "G0108", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}CALLS", + "file": "seed/calls.tabl.asabap", + "description": "Phone calls" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_CALL_CHARGE", + "fields": [ + "CALL_ID", + "CUSTOMER", + "BILLABLE_MIN", + "RATE", + "CHARGE" + ] + } + ], + "out_of_scope": [ + "{{P}}CALLS" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_CALL_CHARGE", + "file": "reference/i_call_charge.ddls.asddls", + "description": "Call charge" + }, + { + "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/eval/G0109/faulty/m0_i_manager.ddls.asddls b/tasks_gen/eval/G0109/faulty/m0_i_manager.ddls.asddls new file mode 100644 index 0000000..f95d602 --- /dev/null +++ b/tasks_gen/eval/G0109/faulty/m0_i_manager.ddls.asddls @@ -0,0 +1,11 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Manager' +define view entity {{P}}I_MANAGER + as select from {{p}}emp +{ + key emp_id as mgr_id, + emp_name as mgr_name, + department as mgr_dept +} +where + is_manager <> 'X' diff --git a/tasks_gen/eval/G0109/faulty/m1_i_employee.ddls.asddls b/tasks_gen/eval/G0109/faulty/m1_i_employee.ddls.asddls new file mode 100644 index 0000000..e3146d5 --- /dev/null +++ b/tasks_gen/eval/G0109/faulty/m1_i_employee.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Employee' +define view entity {{P}}I_EMPLOYEE + as select from {{p}}emp + association [0..1] to {{P}}I_MANAGER as _Manager + on $projection.manager_id <> _Manager.mgr_id +{ + key emp_id as emp_id, + manager_id as manager_id, + emp_name as emp_name, + department as department, + _Manager +} diff --git a/tasks_gen/eval/G0109/faulty/m2_i_manager.ddls.asddls b/tasks_gen/eval/G0109/faulty/m2_i_manager.ddls.asddls new file mode 100644 index 0000000..af1a017 --- /dev/null +++ b/tasks_gen/eval/G0109/faulty/m2_i_manager.ddls.asddls @@ -0,0 +1,11 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Manager' +define view entity {{P}}I_MANAGER + as select from {{p}}emp +{ + key emp_id as mgr_id, + emp_name as mgr_name, + department as mgr_dept +} +where + is_manager = 'Z' diff --git a/tasks_gen/eval/G0109/generation.json b/tasks_gen/eval/G0109/generation.json new file mode 100644 index 0000000..8b38de8 --- /dev/null +++ b/tasks_gen/eval/G0109/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0109", + "pool": "eval", + "object_type": "DDLS", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 85.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 3, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0109/hidden/t31_hidden.clas.abap b/tasks_gen/eval/G0109/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..464666d --- /dev/null +++ b/tasks_gen/eval/G0109/hidden/t31_hidden.clas.abap @@ -0,0 +1,92 @@ +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 employees_shown FOR TESTING. + METHODS employee_values FOR TESTING. + METHODS manager_id_shown FOR TESTING. + METHODS managers_shown FOR TESTING. + METHODS manager_values FOR TESTING. + METHODS path_reads_manager FOR TESTING. + METHODS path_manager_dept FOR TESTING. + METHODS path_only_managers FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_EMPLOYEE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_emp TYPE STANDARD TABLE OF {{p}}emp WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_emp = VALUE #( + ( emp_id = 'E1' manager_id = '' emp_name = 'Anna' department = 'IT' is_manager = 'X' ) + ( emp_id = 'E2' manager_id = 'E1' emp_name = 'Ben' department = 'HR' is_manager = 'X' ) + ( emp_id = 'E3' manager_id = 'E1' emp_name = 'Cara' department = 'IT' is_manager = '' ) + ( emp_id = 'E4' manager_id = 'E2' emp_name = 'Dan' department = 'HR' is_manager = '' ) + ( emp_id = 'E5' manager_id = 'E2' emp_name = 'Eve' department = 'IT' is_manager = '' ) + ( emp_id = 'E6' manager_id = 'E3' emp_name = 'Fay' department = 'IT' is_manager = '' ) ). + go_env->insert_test_data( lt_emp ). + ENDMETHOD. + + METHOD employees_shown. + SELECT COUNT(*) FROM {{p}}i_employee INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_count ). + ENDMETHOD. + + METHOD employee_values. + SELECT SINGLE emp_name, department FROM {{p}}i_employee WHERE emp_id = 'E3' + INTO @DATA(ls_emp). + cl_abap_unit_assert=>assert_equals( exp = 'Cara' act = ls_emp-emp_name ). + cl_abap_unit_assert=>assert_equals( exp = 'IT' act = ls_emp-department ). + ENDMETHOD. + + METHOD manager_id_shown. + SELECT SINGLE manager_id FROM {{p}}i_employee WHERE emp_id = 'E4' + INTO @DATA(lv_manager). + cl_abap_unit_assert=>assert_equals( exp = 'E2' act = lv_manager ). + ENDMETHOD. + + METHOD managers_shown. + SELECT COUNT(*) FROM {{p}}i_manager INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD manager_values. + SELECT SINGLE mgr_name, mgr_dept FROM {{p}}i_manager WHERE mgr_id = 'E2' + INTO @DATA(ls_mgr). + cl_abap_unit_assert=>assert_equals( exp = 'Ben' act = ls_mgr-mgr_name ). + cl_abap_unit_assert=>assert_equals( exp = 'HR' act = ls_mgr-mgr_dept ). + ENDMETHOD. + + METHOD path_reads_manager. + SELECT FROM {{p}}i_employee\_manager + FIELDS mgr_name + WHERE mgr_name <> '' + INTO TABLE @DATA(lt_mgr). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_mgr ) ). + ENDMETHOD. + + METHOD path_manager_dept. + SELECT COUNT(*) FROM {{p}}i_employee\_manager + WHERE mgr_dept = 'IT' + INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. + + METHOD path_only_managers. + SELECT COUNT(*) FROM {{p}}i_employee\_manager + WHERE mgr_id = 'E3' + INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0109/mutation.json b/tasks_gen/eval/G0109/mutation.json new file mode 100644 index 0000000..e73c5b7 --- /dev/null +++ b/tasks_gen/eval/G0109/mutation.json @@ -0,0 +1,44 @@ +{ + "task": "G0109", + "mutants": [ + { + "object": "{{P}}I_MANAGER", + "mutant": "line 11: = -> <> (eq)", + "status": "killed", + "hidden": "3/8", + "failed_tests": [ + "MANAGERS_SHOWN", + "MANAGER_VALUES", + "PATH_MANAGER_DEPT", + "PATH_ONLY_MANAGERS", + "PATH_READS_MANAGER" + ] + }, + { + "object": "{{P}}I_EMPLOYEE", + "mutant": "line 6: = -> <> (eq)", + "status": "killed", + "hidden": "6/8", + "failed_tests": [ + "PATH_MANAGER_DEPT", + "PATH_READS_MANAGER" + ] + }, + { + "object": "{{P}}I_MANAGER", + "mutant": "line 11: 'X' -> 'Z' (lit)", + "status": "killed", + "hidden": "4/8", + "failed_tests": [ + "MANAGERS_SHOWN", + "MANAGER_VALUES", + "PATH_MANAGER_DEPT", + "PATH_READS_MANAGER" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0109/reference/i_employee.ddls.asddls b/tasks_gen/eval/G0109/reference/i_employee.ddls.asddls new file mode 100644 index 0000000..9330452 --- /dev/null +++ b/tasks_gen/eval/G0109/reference/i_employee.ddls.asddls @@ -0,0 +1,13 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Employee' +define view entity {{P}}I_EMPLOYEE + as select from {{p}}emp + association [0..1] to {{P}}I_MANAGER as _Manager + on $projection.manager_id = _Manager.mgr_id +{ + key emp_id as emp_id, + manager_id as manager_id, + emp_name as emp_name, + department as department, + _Manager +} diff --git a/tasks_gen/eval/G0109/reference/i_manager.ddls.asddls b/tasks_gen/eval/G0109/reference/i_manager.ddls.asddls new file mode 100644 index 0000000..786d022 --- /dev/null +++ b/tasks_gen/eval/G0109/reference/i_manager.ddls.asddls @@ -0,0 +1,11 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Manager' +define view entity {{P}}I_MANAGER + as select from {{p}}emp +{ + key emp_id as mgr_id, + emp_name as mgr_name, + department as mgr_dept +} +where + is_manager = 'X' diff --git a/tasks_gen/eval/G0109/reference/t31_test.clas.abap b/tasks_gen/eval/G0109/reference/t31_test.clas.abap new file mode 100644 index 0000000..ba57094 --- /dev/null +++ b/tasks_gen/eval/G0109/reference/t31_test.clas.abap @@ -0,0 +1,47 @@ +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 manager_of_employee FOR TESTING. + METHODS manager_view_shown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_EMPLOYEE' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_emp TYPE STANDARD TABLE OF {{p}}emp WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_emp = VALUE #( + ( emp_id = 'E1' manager_id = '' emp_name = 'Anna' department = 'IT' is_manager = 'X' ) + ( emp_id = 'E2' manager_id = 'E1' emp_name = 'Ben' department = 'HR' is_manager = 'X' ) + ( emp_id = 'E3' manager_id = 'E1' emp_name = 'Cara' department = 'IT' is_manager = '' ) + ( emp_id = 'E4' manager_id = 'E2' emp_name = 'Dan' department = 'HR' is_manager = '' ) + ( emp_id = 'E5' manager_id = 'E2' emp_name = 'Eve' department = 'IT' is_manager = '' ) + ( emp_id = 'E6' manager_id = 'E3' emp_name = 'Fay' department = 'IT' is_manager = '' ) ). + go_env->insert_test_data( lt_emp ). + ENDMETHOD. + + METHOD manager_of_employee. + SELECT FROM {{p}}i_employee\_manager + FIELDS mgr_name + WHERE mgr_name <> '' + INTO TABLE @DATA(lt_mgr). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_mgr ) ). + ENDMETHOD. + + METHOD manager_view_shown. + SELECT COUNT(*) FROM {{p}}i_manager INTO @DATA(lv_count). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0109/seed/emp.tabl.asabap b/tasks_gen/eval/G0109/seed/emp.tabl.asabap new file mode 100644 index 0000000..140ecaa --- /dev/null +++ b/tasks_gen/eval/G0109/seed/emp.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Employee' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}emp { + key client : abap.clnt not null; + key emp_id : abap.char(10) not null; + manager_id : abap.char(10); + emp_name : abap.char(40); + department : abap.char(10); + is_manager : abap.char(1); +} diff --git a/tasks_gen/eval/G0109/seed/seed_data.clas.abap b/tasks_gen/eval/G0109/seed/seed_data.clas.abap new file mode 100644 index 0000000..20989b0 --- /dev/null +++ b/tasks_gen/eval/G0109/seed/seed_data.clas.abap @@ -0,0 +1,23 @@ +CLASS {{p}}seed_data DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + +CLASS {{p}}seed_data IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_emp TYPE STANDARD TABLE OF {{p}}emp WITH EMPTY KEY. + lt_emp = VALUE #( + ( emp_id = 'E1' manager_id = '' emp_name = 'Anna' department = 'IT' is_manager = 'X' ) + ( emp_id = 'E2' manager_id = 'E1' emp_name = 'Ben' department = 'HR' is_manager = 'X' ) + ( emp_id = 'E3' manager_id = 'E1' emp_name = 'Cara' department = 'IT' is_manager = '' ) + ( emp_id = 'E4' manager_id = 'E2' emp_name = 'Dan' department = 'HR' is_manager = '' ) + ( emp_id = 'E5' manager_id = 'E2' emp_name = 'Eve' department = 'IT' is_manager = '' ) + ( emp_id = 'E6' manager_id = 'E3' emp_name = 'Fay' department = 'IT' is_manager = '' ) ). + DELETE FROM {{p}}emp. + INSERT {{p}}emp FROM TABLE @lt_emp. + out->write( 'Seed data for employees inserted.' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0109/spec.md b/tasks_gen/eval/G0109/spec.md new file mode 100644 index 0000000..06aa4f3 --- /dev/null +++ b/tasks_gen/eval/G0109/spec.md @@ -0,0 +1,51 @@ +# 1. Goal +A personnel system stores employees. Each employee can have a manager, who is +also an employee. A report reads the manager data of the employees. The data +must be in the database, in CDS views. The report uses an association in an +ABAP SQL path expression. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}EMP (employee), package $TMP. Key: EMP_ID (CHAR 10). + Fields: MANAGER_ID (CHAR 10, the EMP_ID of the manager), EMP_NAME (CHAR 40), + DEPARTMENT (CHAR 10), IS_MANAGER (CHAR 1, 'X' for a manager). +- The table is filled by the seed class {{P}}SEED_DATA. + +# 4. Contract +- Create the CDS view entity {{P}}I_MANAGER in package $TMP. + Scalar elements: MGR_ID (key), MGR_NAME, MGR_DEPT. +- Create the CDS view entity {{P}}I_EMPLOYEE in package $TMP. + Scalar elements: EMP_ID (key), MANAGER_ID, EMP_NAME, DEPARTMENT. +- {{P}}I_EMPLOYEE also has the exposed association _MANAGER. _MANAGER is an + element of the view. It is not a scalar element. +- Both views have no authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. {{P}}I_MANAGER shows one row for each row of {{P}}EMP with IS_MANAGER = 'X'. + MGR_ID, MGR_NAME and MGR_DEPT have the values of EMP_ID, EMP_NAME and + DEPARTMENT of that employee. +2. {{P}}I_EMPLOYEE shows one row for each row of {{P}}EMP. EMP_ID, MANAGER_ID, + EMP_NAME and DEPARTMENT have the values of the employee. +3. The association _MANAGER leads from an employee to the manager of the + employee. The manager is the row of {{P}}I_MANAGER whose MGR_ID is the + MANAGER_ID of the employee. An employee whose MANAGER_ID is empty or has no + such row has no manager. +4. _MANAGER is part of the public interface of {{P}}I_EMPLOYEE. An ABAP SQL + statement reads the manager data with the path expression + {{P}}I_EMPLOYEE\_MANAGER. Example: + SELECT FROM {{P}}I_EMPLOYEE\_MANAGER FIELDS mgr_name + INTO TABLE @DATA(lt_name). + The elements of the path expression are the elements of the manager, not + the elements of the employee. + +# 6. Constraints +- Release target: 8.16. +- Out of scope: do not change {{P}}EMP. +- Use ABAP SQL and CDS only. + +# 7. Acceptance +- Both views are active and have no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/eval/G0109/task.json b/tasks_gen/eval/G0109/task.json new file mode 100644 index 0000000..c1f22f2 --- /dev/null +++ b/tasks_gen/eval/G0109/task.json @@ -0,0 +1,80 @@ +{ + "id": "G0109", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}EMP", + "file": "seed/emp.tabl.asabap", + "description": "Employee" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_DATA", + "file": "seed/seed_data.clas.abap", + "description": "Seed data for employees", + "run": true + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_MANAGER", + "fields": [ + "MGR_ID", + "MGR_NAME", + "MGR_DEPT" + ] + }, + { + "type": "DDLS", + "name": "{{P}}I_EMPLOYEE", + "fields": [ + "EMP_ID", + "MANAGER_ID", + "EMP_NAME", + "DEPARTMENT" + ] + } + ], + "out_of_scope": [ + "{{P}}EMP" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "T31 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_MANAGER", + "file": "reference/i_manager.ddls.asddls", + "description": "Managers" + }, + { + "type": "DDLS", + "name": "{{P}}I_EMPLOYEE", + "file": "reference/i_employee.ddls.asddls", + "description": "Employees with exposed manager association" + }, + { + "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/eval/G0110/faulty/m1_i_prod_sales.ddls.asddls b/tasks_gen/eval/G0110/faulty/m1_i_prod_sales.ddls.asddls new file mode 100644 index 0000000..ac42d49 --- /dev/null +++ b/tasks_gen/eval/G0110/faulty/m1_i_prod_sales.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Product sales' +define view entity {{P}}I_PROD_SALES + as select from {{p}}prod as prod + inner join {{p}}sale as sale on sale.prod_id <> prod.prod_id +{ + key prod.prod_id as prod_id, + prod.prod_name as prod_name, + count( * ) as item_count, + sum( sale.quantity ) as total_qty +} +group by + prod.prod_id, + prod.prod_name +having + sum( sale.quantity ) >= 100 diff --git a/tasks_gen/eval/G0110/faulty/m2_i_prod_sales.ddls.asddls b/tasks_gen/eval/G0110/faulty/m2_i_prod_sales.ddls.asddls new file mode 100644 index 0000000..4be5830 --- /dev/null +++ b/tasks_gen/eval/G0110/faulty/m2_i_prod_sales.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Product sales' +define view entity {{P}}I_PROD_SALES + as select from {{p}}prod as prod + inner join {{p}}sale as sale on sale.prod_id = prod.prod_id +{ + key prod.prod_id as prod_id, + prod.prod_name as prod_name, + count( * ) as item_count, + max( sale.quantity ) as total_qty +} +group by + prod.prod_id, + prod.prod_name +having + sum( sale.quantity ) >= 100 diff --git a/tasks_gen/eval/G0110/faulty/m3_i_prod_sales.ddls.asddls b/tasks_gen/eval/G0110/faulty/m3_i_prod_sales.ddls.asddls new file mode 100644 index 0000000..3e49c8a --- /dev/null +++ b/tasks_gen/eval/G0110/faulty/m3_i_prod_sales.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Product sales' +define view entity {{P}}I_PROD_SALES + as select from {{p}}prod as prod + inner join {{p}}sale as sale on sale.prod_id = prod.prod_id +{ + key prod.prod_id as prod_id, + prod.prod_name as prod_name, + count( * ) as item_count, + sum( sale.quantity ) as total_qty +} +group by + prod.prod_id, + prod.prod_name +having + sum( sale.quantity ) < 100 diff --git a/tasks_gen/eval/G0110/faulty/m4_i_prod_sales.ddls.asddls b/tasks_gen/eval/G0110/faulty/m4_i_prod_sales.ddls.asddls new file mode 100644 index 0000000..4b518b5 --- /dev/null +++ b/tasks_gen/eval/G0110/faulty/m4_i_prod_sales.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Product sales' +define view entity {{P}}I_PROD_SALES + as select from {{p}}prod as prod + inner join {{p}}sale as sale on sale.prod_id = prod.prod_id +{ + key prod.prod_id as prod_id, + prod.prod_name as prod_name, + count( * ) as item_count, + sum( sale.quantity ) as total_qty +} +group by + prod.prod_id, + prod.prod_name +having + sum( sale.quantity ) >= 101 diff --git a/tasks_gen/eval/G0110/generation.json b/tasks_gen/eval/G0110/generation.json new file mode 100644 index 0000000..58a1652 --- /dev/null +++ b/tasks_gen/eval/G0110/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0110", + "pool": "eval", + "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/eval/G0110/hidden/t16_hidden.clas.abap b/tasks_gen/eval/G0110/hidden/t16_hidden.clas.abap new file mode 100644 index 0000000..51d22a7 --- /dev/null +++ b/tasks_gen/eval/G0110/hidden/t16_hidden.clas.abap @@ -0,0 +1,74 @@ +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 boundary_included FOR TESTING. + METHODS below_threshold_excluded FOR TESTING. + METHODS product_without_items FOR TESTING. + METHODS totals FOR TESTING. + METHODS product_name FOR TESTING. + METHODS row_count FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PROD_SALES' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + DATA lt_prod TYPE STANDARD TABLE OF {{p}}prod WITH EMPTY KEY. + DATA lt_sale TYPE STANDARD TABLE OF {{p}}sale WITH EMPTY KEY. + go_env->clear_doubles( ). + lt_prod = VALUE #( ( prod_id = 'P1' prod_name = 'Widget' ) + ( prod_id = 'P2' prod_name = 'Gadget' ) + ( prod_id = 'P3' prod_name = 'Gizmo' ) + ( prod_id = 'P4' prod_name = 'Spare' ) ). + lt_sale = VALUE #( ( sale_id = 'S1' item_no = 1 prod_id = 'P1' quantity = 60 ) + ( sale_id = 'S1' item_no = 2 prod_id = 'P1' quantity = 50 ) + ( sale_id = 'S2' item_no = 1 prod_id = 'P2' quantity = 40 ) + ( sale_id = 'S2' item_no = 2 prod_id = 'P2' quantity = 30 ) + ( sale_id = 'S3' item_no = 1 prod_id = 'P3' quantity = 100 ) ). + go_env->insert_test_data( lt_prod ). + go_env->insert_test_data( lt_sale ). + ENDMETHOD. + + METHOD boundary_included. + SELECT SINGLE total_qty FROM {{p}}i_prod_sales WHERE prod_id = 'P3' INTO @DATA(lv_total). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lv_total ). + ENDMETHOD. + + METHOD below_threshold_excluded. + SELECT COUNT(*) FROM {{p}}i_prod_sales WHERE prod_id = 'P2' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD product_without_items. + SELECT COUNT(*) FROM {{p}}i_prod_sales WHERE prod_id = 'P4' INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rows ). + ENDMETHOD. + + METHOD totals. + SELECT SINGLE item_count, total_qty FROM {{p}}i_prod_sales WHERE prod_id = 'P1' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-item_count ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = ls_row-total_qty ). + ENDMETHOD. + + METHOD product_name. + SELECT SINGLE prod_name FROM {{p}}i_prod_sales WHERE prod_id = 'P1' INTO @DATA(lv_name). + cl_abap_unit_assert=>assert_equals( exp = 'Widget' act = lv_name ). + ENDMETHOD. + + METHOD row_count. + SELECT COUNT(*) FROM {{p}}i_prod_sales INTO @DATA(lv_rows). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rows ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0110/mutation.json b/tasks_gen/eval/G0110/mutation.json new file mode 100644 index 0000000..1951030 --- /dev/null +++ b/tasks_gen/eval/G0110/mutation.json @@ -0,0 +1,61 @@ +{ + "task": "G0110", + "mutants": [ + { + "object": "{{P}}I_PROD_SALES", + "mutant": "line 5: inner join -> left outer join (join)", + "status": "survived", + "hidden": "6/6", + "failed_tests": [] + }, + { + "object": "{{P}}I_PROD_SALES", + "mutant": "line 5: = -> <> (eq)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "BELOW_THRESHOLD_EXCLUDED", + "BOUNDARY_INCLUDED", + "PRODUCT_WITHOUT_ITEMS", + "ROW_COUNT", + "TOTALS" + ] + }, + { + "object": "{{P}}I_PROD_SALES", + "mutant": "line 10: sum( -> max( (agg)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "TOTALS" + ] + }, + { + "object": "{{P}}I_PROD_SALES", + "mutant": "line 16: >= -> < (rel)", + "status": "killed", + "hidden": "1/6", + "failed_tests": [ + "BELOW_THRESHOLD_EXCLUDED", + "BOUNDARY_INCLUDED", + "PRODUCT_NAME", + "ROW_COUNT", + "TOTALS" + ] + }, + { + "object": "{{P}}I_PROD_SALES", + "mutant": "line 16: 100 -> 101 (const)", + "status": "killed", + "hidden": "4/6", + "failed_tests": [ + "BOUNDARY_INCLUDED", + "ROW_COUNT" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0110/reference/i_prod_sales.ddls.asddls b/tasks_gen/eval/G0110/reference/i_prod_sales.ddls.asddls new file mode 100644 index 0000000..abc83c1 --- /dev/null +++ b/tasks_gen/eval/G0110/reference/i_prod_sales.ddls.asddls @@ -0,0 +1,16 @@ +@AccessControl.authorizationCheck: #NOT_REQUIRED +@EndUserText.label: 'Product sales' +define view entity {{P}}I_PROD_SALES + as select from {{p}}prod as prod + inner join {{p}}sale as sale on sale.prod_id = prod.prod_id +{ + key prod.prod_id as prod_id, + prod.prod_name as prod_name, + count( * ) as item_count, + sum( sale.quantity ) as total_qty +} +group by + prod.prod_id, + prod.prod_name +having + sum( sale.quantity ) >= 100 diff --git a/tasks_gen/eval/G0110/reference/t16_test.clas.abap b/tasks_gen/eval/G0110/reference/t16_test.clas.abap new file mode 100644 index 0000000..1f21ee2 --- /dev/null +++ b/tasks_gen/eval/G0110/reference/t16_test.clas.abap @@ -0,0 +1,38 @@ +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 totals_of_one_product FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t16_test IMPLEMENTATION. + METHOD class_setup. + go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_PROD_SALES' ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD setup. + go_env->clear_doubles( ). + ENDMETHOD. + + METHOD totals_of_one_product. + DATA lt_prod TYPE STANDARD TABLE OF {{p}}prod WITH EMPTY KEY. + DATA lt_sale TYPE STANDARD TABLE OF {{p}}sale WITH EMPTY KEY. + lt_prod = VALUE #( ( prod_id = 'A' prod_name = 'Alpha' ) ). + lt_sale = VALUE #( ( sale_id = 'S1' item_no = 1 prod_id = 'A' quantity = 60 ) + ( sale_id = 'S1' item_no = 2 prod_id = 'A' quantity = 50 ) ). + go_env->insert_test_data( lt_prod ). + go_env->insert_test_data( lt_sale ). + SELECT SINGLE item_count, total_qty FROM {{p}}i_prod_sales WHERE prod_id = 'A' + INTO @DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-item_count ). + cl_abap_unit_assert=>assert_equals( exp = 110 act = ls_row-total_qty ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0110/seed/prod.tabl.asabap b/tasks_gen/eval/G0110/seed/prod.tabl.asabap new file mode 100644 index 0000000..8cebd45 --- /dev/null +++ b/tasks_gen/eval/G0110/seed/prod.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Product master' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}prod { + key client : abap.clnt not null; + key prod_id : abap.char(10) not null; + prod_name : abap.char(40); +} diff --git a/tasks_gen/eval/G0110/seed/sale.tabl.asabap b/tasks_gen/eval/G0110/seed/sale.tabl.asabap new file mode 100644 index 0000000..9ff096f --- /dev/null +++ b/tasks_gen/eval/G0110/seed/sale.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Sales item' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}sale { + key client : abap.clnt not null; + key sale_id : abap.char(10) not null; + key item_no : abap.int4 not null; + prod_id : abap.char(10); + quantity : abap.int4; +} diff --git a/tasks_gen/eval/G0110/spec.md b/tasks_gen/eval/G0110/spec.md new file mode 100644 index 0000000..bb948c1 --- /dev/null +++ b/tasks_gen/eval/G0110/spec.md @@ -0,0 +1,37 @@ +# 1. Goal +A sales analyst wants to know which products sell well. Show each product with +the number of sales items and the total sales quantity. Show only products whose +total sales quantity is at least 100. 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}}PROD (product master), package $TMP. Key: PROD_ID (CHAR 10). + Fields: PROD_NAME (CHAR 40). +- Table {{P}}SALE (sales item), package $TMP. Key: SALE_ID (CHAR 10), ITEM_NO (INT4). + Fields: PROD_ID (CHAR 10), QUANTITY (INT4). + +# 4. Contract +- Create the CDS view entity {{P}}I_PROD_SALES in package $TMP. +- Elements, with these names: PROD_ID (key), PROD_NAME, ITEM_COUNT, TOTAL_QTY. +- No authorization check (#NOT_REQUIRED). + +# 5. Business rules +1. Show one row for each product that has at least one sales item and whose total + sales quantity is at least 100. Do not show a product with no sales item. + Do not show a product whose total sales quantity is less than 100. +2. ITEM_COUNT is the number of sales items of the product. +3. TOTAL_QTY is the sum of QUANTITY of all sales items of the product. +4. PROD_NAME is the name of the product. + +# 6. Constraints +- Release target: 7.58. +- Out of scope: do not change {{P}}PROD and {{P}}SALE. +- Coding standards: Clean ABAP. + +# 7. Acceptance +- The view is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class. diff --git a/tasks_gen/eval/G0110/task.json b/tasks_gen/eval/G0110/task.json new file mode 100644 index 0000000..d9aa880 --- /dev/null +++ b/tasks_gen/eval/G0110/task.json @@ -0,0 +1,65 @@ +{ + "id": "G0110", + "category": "B", + "object_type": "DDLS", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}PROD", + "file": "seed/prod.tabl.asabap", + "description": "Product master" + }, + { + "type": "TABL", + "name": "{{P}}SALE", + "file": "seed/sale.tabl.asabap", + "description": "Sales item" + } + ], + "contract": [ + { + "type": "DDLS", + "name": "{{P}}I_PROD_SALES", + "fields": [ + "PROD_ID", + "PROD_NAME", + "ITEM_COUNT", + "TOTAL_QTY" + ] + } + ], + "out_of_scope": [ + "{{P}}PROD", + "{{P}}SALE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T16_HIDDEN", + "file": "hidden/t16_hidden.clas.abap", + "description": "T16 hidden tests" + } + ], + "reference": [ + { + "type": "DDLS", + "name": "{{P}}I_PROD_SALES", + "file": "reference/i_prod_sales.ddls.asddls", + "description": "Product sales" + }, + { + "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/eval/G0111/faulty/m1_court_use.prog.abap b/tasks_gen/eval/G0111/faulty/m1_court_use.prog.abap new file mode 100644 index 0000000..b1ba448 --- /dev/null +++ b/tasks_gen/eval/G0111/faulty/m1_court_use.prog.abap @@ -0,0 +1,136 @@ +REPORT {{p}}court_use. + +DATA gv_sport TYPE {{p}}court-sport. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_sport FOR gv_sport. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + duration TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_db, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + dur_min TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_db, + tt_db TYPE STANDARD TABLE OF ty_db WITH EMPTY KEY. + TYPES tt_sport TYPE RANGE OF {{p}}court-sport. + + METHODS select_usage + IMPORTING iv_from TYPE d + iv_to TYPE d + it_sport TYPE tt_sport + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_usage. + DATA lt_db TYPE tt_db. + SELECT c~court_name, + c~sport, + COUNT( * ) AS bookings, + SUM( b~duration_min ) AS dur_min, + SUM( b~fee ) AS total + FROM {{p}}booking AS b + INNER JOIN {{p}}court AS c ON c~court_id = b~court_id + WHERE b~booking_date BETWEEN @iv_from AND @iv_to + OR c~sport IN @it_sport + GROUP BY c~court_name, c~sport + ORDER BY total DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @lt_db. + + rt_lines = VALUE #( FOR ls_db IN lt_db + ( court_name = ls_db-court_name + sport = ls_db-sport + bookings = ls_db-bookings + duration = ls_db-dur_min / 60 + total = ls_db-total ) ). + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_usage( iv_from = p_from + iv_to = p_to + it_sport = s_sport[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_court FOR TESTING. + METHODS duration_calc FOR TESTING. + METHODS sport_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}COURT' ) ( '{{P}}BOOKING' ) ) ). + DATA lt_court TYPE STANDARD TABLE OF {{p}}court WITH EMPTY KEY. + lt_court = VALUE #( ( court_id = 1 court_name = 'A' sport = 'TENNIS' ) + ( court_id = 2 court_name = 'B' sport = 'SQUASH' ) ). + go_env->insert_test_data( lt_court ). + DATA lt_booking TYPE STANDARD TABLE OF {{p}}booking WITH EMPTY KEY. + lt_booking = VALUE #( ( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' ) + ( booking_id = 2 court_id = 1 booking_date = '20260120' duration_min = 30 fee = '15.00' ) + ( booking_id = 3 court_id = 2 booking_date = '20260115' duration_min = 90 fee = '50.00' ) ). + go_env->insert_test_data( lt_booking ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_court. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( 50 ) act = lt_lines[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-bookings ). + ENDMETHOD. + + METHOD duration_calc. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 1 ]-duration ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 2 ]-duration ). + ENDMETHOD. + + METHOD sport_filter. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' + it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'SQUASH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0111/faulty/m2_court_use.prog.abap b/tasks_gen/eval/G0111/faulty/m2_court_use.prog.abap new file mode 100644 index 0000000..6acf519 --- /dev/null +++ b/tasks_gen/eval/G0111/faulty/m2_court_use.prog.abap @@ -0,0 +1,136 @@ +REPORT {{p}}court_use. + +DATA gv_sport TYPE {{p}}court-sport. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_sport FOR gv_sport. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + duration TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_db, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + dur_min TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_db, + tt_db TYPE STANDARD TABLE OF ty_db WITH EMPTY KEY. + TYPES tt_sport TYPE RANGE OF {{p}}court-sport. + + METHODS select_usage + IMPORTING iv_from TYPE d + iv_to TYPE d + it_sport TYPE tt_sport + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_usage. + DATA lt_db TYPE tt_db. + SELECT c~court_name, + c~sport, + COUNT( * ) AS bookings, + SUM( b~duration_min ) AS dur_min, + SUM( b~fee ) AS total + FROM {{p}}booking AS b + INNER JOIN {{p}}court AS c ON c~court_id = b~court_id + WHERE b~booking_date BETWEEN @iv_from AND @iv_to + AND c~sport IN @it_sport + GROUP BY c~court_name, c~sport + ORDER BY total DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @lt_db. + + rt_lines = VALUE #( FOR ls_db IN lt_db + ( court_name = ls_db-court_name + sport = ls_db-sport + bookings = ls_db-bookings + duration = ls_db-dur_min / 61 + total = ls_db-total ) ). + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_usage( iv_from = p_from + iv_to = p_to + it_sport = s_sport[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_court FOR TESTING. + METHODS duration_calc FOR TESTING. + METHODS sport_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}COURT' ) ( '{{P}}BOOKING' ) ) ). + DATA lt_court TYPE STANDARD TABLE OF {{p}}court WITH EMPTY KEY. + lt_court = VALUE #( ( court_id = 1 court_name = 'A' sport = 'TENNIS' ) + ( court_id = 2 court_name = 'B' sport = 'SQUASH' ) ). + go_env->insert_test_data( lt_court ). + DATA lt_booking TYPE STANDARD TABLE OF {{p}}booking WITH EMPTY KEY. + lt_booking = VALUE #( ( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' ) + ( booking_id = 2 court_id = 1 booking_date = '20260120' duration_min = 30 fee = '15.00' ) + ( booking_id = 3 court_id = 2 booking_date = '20260115' duration_min = 90 fee = '50.00' ) ). + go_env->insert_test_data( lt_booking ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_court. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( 50 ) act = lt_lines[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-bookings ). + ENDMETHOD. + + METHOD duration_calc. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 1 ]-duration ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 2 ]-duration ). + ENDMETHOD. + + METHOD sport_filter. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' + it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'SQUASH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0111/generation.json b/tasks_gen/eval/G0111/generation.json new file mode 100644 index 0000000..4e64b06 --- /dev/null +++ b/tasks_gen/eval/G0111/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0111", + "pool": "eval", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 2, + "killed": 2, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0111/hidden/court_hidden.clas.abap b/tasks_gen/eval/G0111/hidden/court_hidden.clas.abap new file mode 100644 index 0000000..461100c --- /dev/null +++ b/tasks_gen/eval/G0111/hidden/court_hidden.clas.abap @@ -0,0 +1,118 @@ +CLASS {{p}}court_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_sport TYPE RANGE OF {{p}}court-sport. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_sport TYPE tt_sport OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_courts FOR TESTING. + METHODS totals_and_counts FOR TESTING. + METHODS duration_calc FOR TESTING. + METHODS sport_filter FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS december_only FOR TESTING. +ENDCLASS. + + +CLASS {{p}}court_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}court_use WITH p_from = iv_from WITH p_to = iv_to WITH s_sport IN it_sport AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_courts. + cl_abap_unit_assert=>assert_equals( + exp = 3 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ). + ENDMETHOD. + + METHOD totals_and_counts. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `Center Court` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `75.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOOKINGS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `Hall A` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `35.00` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TOTAL` ) ). + cl_abap_unit_assert=>assert_equals( exp = `North Court` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `BOOKINGS` ) ). + ENDMETHOD. + + METHOD duration_calc. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `2.50` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DURATION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.25` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DURATION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.00` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DURATION` ) ). + ENDMETHOD. + + METHOD sport_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'TENNIS' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `Center Court` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `North Court` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COURT_NAME` ) ). + ENDMETHOD. + + METHOD date_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260228' ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `North Court` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `90.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOOKINGS` ) ). + ENDMETHOD. + + METHOD december_only. + DATA(lr_data) = run( iv_from = '20251201' iv_to = '20251231' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `Hall B` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `25.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0111/mutation.json b/tasks_gen/eval/G0111/mutation.json new file mode 100644 index 0000000..4f38bf5 --- /dev/null +++ b/tasks_gen/eval/G0111/mutation.json @@ -0,0 +1,39 @@ +{ + "task": "G0111", + "mutants": [ + { + "object": "{{P}}COURT_USE", + "mutant": "line 50: AND -> OR (logic)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}COURT_USE", + "mutant": "line 51: AND -> OR (logic)", + "status": "killed", + "hidden": "0/6", + "failed_tests": [ + "ALL_COURTS", + "DATE_FILTER", + "DECEMBER_ONLY", + "DURATION_CALC", + "SPORT_FILTER", + "TOTALS_AND_COUNTS" + ] + }, + { + "object": "{{P}}COURT_USE", + "mutant": "line 60: 60 -> 61 (const)", + "status": "killed", + "hidden": "5/6", + "failed_tests": [ + "DURATION_CALC" + ] + } + ], + "valid": 2, + "killed": 2, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0111/reference/court_use.prog.abap b/tasks_gen/eval/G0111/reference/court_use.prog.abap new file mode 100644 index 0000000..048157d --- /dev/null +++ b/tasks_gen/eval/G0111/reference/court_use.prog.abap @@ -0,0 +1,136 @@ +REPORT {{p}}court_use. + +DATA gv_sport TYPE {{p}}court-sport. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_sport FOR gv_sport. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + duration TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + BEGIN OF ty_db, + court_name TYPE {{p}}court-court_name, + sport TYPE {{p}}court-sport, + bookings TYPE i, + dur_min TYPE p LENGTH 8 DECIMALS 2, + total TYPE p LENGTH 8 DECIMALS 2, + END OF ty_db, + tt_db TYPE STANDARD TABLE OF ty_db WITH EMPTY KEY. + TYPES tt_sport TYPE RANGE OF {{p}}court-sport. + + METHODS select_usage + IMPORTING iv_from TYPE d + iv_to TYPE d + it_sport TYPE tt_sport + RETURNING VALUE(rt_lines) TYPE tt_line. + + METHODS display + CHANGING ct_lines TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_usage. + DATA lt_db TYPE tt_db. + SELECT c~court_name, + c~sport, + COUNT( * ) AS bookings, + SUM( b~duration_min ) AS dur_min, + SUM( b~fee ) AS total + FROM {{p}}booking AS b + INNER JOIN {{p}}court AS c ON c~court_id = b~court_id + WHERE b~booking_date BETWEEN @iv_from AND @iv_to + AND c~sport IN @it_sport + GROUP BY c~court_name, c~sport + ORDER BY total DESCENDING + INTO CORRESPONDING FIELDS OF TABLE @lt_db. + + rt_lines = VALUE #( FOR ls_db IN lt_db + ( court_name = ls_db-court_name + sport = ls_db-sport + bookings = ls_db-bookings + duration = ls_db-dur_min / 60 + total = ls_db-total ) ). + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_usage( iv_from = p_from + iv_to = p_to + it_sport = s_sport[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_court FOR TESTING. + METHODS duration_calc FOR TESTING. + METHODS sport_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}COURT' ) ( '{{P}}BOOKING' ) ) ). + DATA lt_court TYPE STANDARD TABLE OF {{p}}court WITH EMPTY KEY. + lt_court = VALUE #( ( court_id = 1 court_name = 'A' sport = 'TENNIS' ) + ( court_id = 2 court_name = 'B' sport = 'SQUASH' ) ). + go_env->insert_test_data( lt_court ). + DATA lt_booking TYPE STANDARD TABLE OF {{p}}booking WITH EMPTY KEY. + lt_booking = VALUE #( ( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' ) + ( booking_id = 2 court_id = 1 booking_date = '20260120' duration_min = 30 fee = '15.00' ) + ( booking_id = 3 court_id = 2 booking_date = '20260115' duration_min = 90 fee = '50.00' ) ). + go_env->insert_test_data( lt_booking ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_court. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( 50 ) act = lt_lines[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-bookings ). + ENDMETHOD. + + METHOD duration_calc. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 1 ]-duration ). + cl_abap_unit_assert=>assert_equals( + exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 2 ]-duration ). + ENDMETHOD. + + METHOD sport_filter. + DATA(lt_lines) = NEW lcl_report( )->select_usage( + iv_from = '20260101' iv_to = '20260131' + it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'SQUASH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0111/seed/booking.tabl.asabap b/tasks_gen/eval/G0111/seed/booking.tabl.asabap new file mode 100644 index 0000000..2d0efe8 --- /dev/null +++ b/tasks_gen/eval/G0111/seed/booking.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Booking' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}booking { + key client : abap.clnt not null; + key booking_id : abap.int4 not null; + court_id : abap.int4; + booking_date : abap.dats; + duration_min : abap.int4; + fee : abap.dec(15,2); +} diff --git a/tasks_gen/eval/G0111/seed/court.tabl.asabap b/tasks_gen/eval/G0111/seed/court.tabl.asabap new file mode 100644 index 0000000..608e310 --- /dev/null +++ b/tasks_gen/eval/G0111/seed/court.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Court' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}court { + key client : abap.clnt not null; + key court_id : abap.int4 not null; + court_name : abap.char(40); + sport : abap.char(20); +} diff --git a/tasks_gen/eval/G0111/seed/court_seed.clas.abap b/tasks_gen/eval/G0111/seed/court_seed.clas.abap new file mode 100644 index 0000000..3656591 --- /dev/null +++ b/tasks_gen/eval/G0111/seed/court_seed.clas.abap @@ -0,0 +1,25 @@ +CLASS {{p}}court_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}court_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}booking. + DELETE FROM {{p}}court. + INSERT {{p}}court FROM TABLE @( VALUE #( + ( court_id = 1 court_name = 'Center Court' sport = 'TENNIS' ) + ( court_id = 2 court_name = 'North Court' sport = 'TENNIS' ) + ( court_id = 3 court_name = 'Hall A' sport = 'BADMINTON' ) + ( court_id = 4 court_name = 'Hall B' sport = 'SQUASH' ) ) ). + INSERT {{p}}booking FROM TABLE @( VALUE #( + ( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' ) + ( booking_id = 2 court_id = 1 booking_date = '20260115' duration_min = 90 fee = '45.00' ) + ( booking_id = 3 court_id = 2 booking_date = '20260112' duration_min = 60 fee = '30.00' ) + ( booking_id = 4 court_id = 3 booking_date = '20260111' duration_min = 45 fee = '20.00' ) + ( booking_id = 5 court_id = 3 booking_date = '20260120' duration_min = 30 fee = '15.00' ) + ( booking_id = 6 court_id = 4 booking_date = '20251220' duration_min = 60 fee = '25.00' ) + ( booking_id = 7 court_id = 2 booking_date = '20260205' duration_min = 120 fee = '60.00' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0111/spec.md b/tasks_gen/eval/G0111/spec.md new file mode 100644 index 0000000..6a54429 --- /dev/null +++ b/tasks_gen/eval/G0111/spec.md @@ -0,0 +1,46 @@ +# 1. Goal +A sports centre rents its courts to clubs. The manager needs a report about the +use of the courts. The report shows, for each court, the number of bookings, +the booked time in hours and the total fee for a period. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}COURT exists in package $TMP. It contains data. + Fields: COURT_ID (INT4, key), COURT_NAME (CHAR 40), SPORT (CHAR 20). +- Table {{P}}BOOKING exists in package $TMP. It contains data. + Fields: BOOKING_ID (INT4, key), COURT_ID (INT4), BOOKING_DATE (DATS), + DURATION_MIN (INT4, the booked time in minutes), FEE (DEC 15,2). +- A booking belongs to exactly one court. The field BOOKING.COURT_ID refers to + the field COURT.COURT_ID. + +# 4. Contract +- Create the executable program {{P}}COURT_USE in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first booking date to include. + - Parameter P_TO, type DATS, mandatory: the last booking date to include. + - Select-option S_SPORT for the sport of the court. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: COURT_NAME, SPORT, BOOKINGS, DURATION, TOTAL. + +# 5. Business rules +1. Include a booking only if BOOKING_DATE is on or after P_FROM and on or + before P_TO. +2. Include a booking only if the SPORT of its court is in S_SPORT. +3. Show one line for each court that has at least one included booking. +4. BOOKINGS is the number of included bookings of the court. +5. DURATION is the sum of DURATION_MIN of the included bookings, divided by 60. + DURATION has two decimal places. +6. TOTAL is the sum of FEE of the included bookings. +7. Sort the lines by TOTAL, the highest total first. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}COURT and {{P}}BOOKING. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/eval/G0111/task.json b/tasks_gen/eval/G0111/task.json new file mode 100644 index 0000000..27dfda3 --- /dev/null +++ b/tasks_gen/eval/G0111/task.json @@ -0,0 +1,68 @@ +{ + "id": "G0111", + "category": "B", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}COURT", + "file": "seed/court.tabl.asabap", + "description": "Courts of the sports centre" + }, + { + "type": "TABL", + "name": "{{P}}BOOKING", + "file": "seed/booking.tabl.asabap", + "description": "Court bookings" + }, + { + "type": "CLAS", + "name": "{{P}}COURT_SEED", + "file": "seed/court_seed.clas.abap", + "description": "Court and booking seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}COURT_USE", + "parameters": [ + "p_from", + "p_to", + "s_sport" + ] + } + ], + "out_of_scope": [ + "{{P}}COURT", + "{{P}}BOOKING" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}COURT_HIDDEN", + "file": "hidden/court_hidden.clas.abap", + "description": "Hidden tests for the court usage report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}COURT_USE", + "file": "reference/court_use.prog.abap", + "description": "Court usage report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0112/faulty/m1_waste_cost.prog.abap b/tasks_gen/eval/G0112/faulty/m1_waste_cost.prog.abap new file mode 100644 index 0000000..7d4d80d --- /dev/null +++ b/tasks_gen/eval/G0112/faulty/m1_waste_cost.prog.abap @@ -0,0 +1,143 @@ +REPORT {{p}}waste_cost. + +DATA gv_district TYPE {{p}}pickup-district. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_dist FOR gv_district. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + weight TYPE {{p}}pickup-weight_kg, + cost TYPE {{p}}price-price_per_kg, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_district TYPE RANGE OF {{p}}pickup-district. + METHODS select_costs + IMPORTING iv_from TYPE d + iv_to TYPE d + it_district TYPE tt_district + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_raw, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + price TYPE {{p}}price-price_per_kg, + weight TYPE {{p}}pickup-weight_kg, + END OF ty_raw, + tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY. + METHODS district_range + IMPORTING it_district TYPE tt_district + RETURNING VALUE(rt_district) TYPE tt_district. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD district_range. + rt_district = it_district. + IF rt_district IS INITIAL. + rt_district = VALUE #( ( sign = 'I' option = 'CP' low = '*' ) ). + ENDIF. + ENDMETHOD. + + METHOD select_costs. + DATA lt_raw TYPE tt_raw. + DATA(lt_district) = district_range( it_district ). + + SELECT p~district, p~waste_type, r~price_per_kg AS price, + SUM( p~weight_kg ) AS weight + FROM {{p}}pickup AS p + INNER JOIN {{p}}price AS r ON r~waste_type = p~waste_type + WHERE p~pickup_date BETWEEN @iv_from AND @iv_to + OR p~district IN @lt_district + GROUP BY p~district, p~waste_type, r~price_per_kg + INTO CORRESPONDING FIELDS OF TABLE @lt_raw. + + rt_lines = VALUE #( FOR ls_raw IN lt_raw + ( district = ls_raw-district + waste_type = ls_raw-waste_type + weight = ls_raw-weight + cost = round( val = ls_raw-weight * ls_raw-price dec = 2 ) ) ). + + SORT rt_lines BY cost DESCENDING district waste_type. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_costs( + iv_from = p_from iv_to = p_to it_district = s_dist[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS costs_per_group FOR TESTING. + METHODS date_range FOR TESTING. + METHODS district_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}PICKUP' ) ( '{{P}}PRICE' ) ) ). + DATA lt_price TYPE STANDARD TABLE OF {{p}}price WITH EMPTY KEY. + lt_price = VALUE #( ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) ). + go_env->insert_test_data( lt_price ). + DATA lt_pickup TYPE STANDARD TABLE OF {{p}}pickup WITH EMPTY KEY. + lt_pickup = VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) ). + go_env->insert_test_data( lt_pickup ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD costs_per_group. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_lines[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}price-price_per_kg( '30.00' ) act = lt_lines[ 1 ]-cost ). + ENDMETHOD. + + METHOD date_range. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260201' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '50.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. + + METHOD district_filter. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'S' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '300.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/faulty/m2_waste_cost.prog.abap b/tasks_gen/eval/G0112/faulty/m2_waste_cost.prog.abap new file mode 100644 index 0000000..6daee55 --- /dev/null +++ b/tasks_gen/eval/G0112/faulty/m2_waste_cost.prog.abap @@ -0,0 +1,143 @@ +REPORT {{p}}waste_cost. + +DATA gv_district TYPE {{p}}pickup-district. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_dist FOR gv_district. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + weight TYPE {{p}}pickup-weight_kg, + cost TYPE {{p}}price-price_per_kg, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_district TYPE RANGE OF {{p}}pickup-district. + METHODS select_costs + IMPORTING iv_from TYPE d + iv_to TYPE d + it_district TYPE tt_district + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_raw, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + price TYPE {{p}}price-price_per_kg, + weight TYPE {{p}}pickup-weight_kg, + END OF ty_raw, + tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY. + METHODS district_range + IMPORTING it_district TYPE tt_district + RETURNING VALUE(rt_district) TYPE tt_district. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD district_range. + rt_district = it_district. + IF rt_district IS INITIAL. + rt_district = VALUE #( ( sign = 'I' option = 'CP' low = '*' ) ). + ENDIF. + ENDMETHOD. + + METHOD select_costs. + DATA lt_raw TYPE tt_raw. + DATA(lt_district) = district_range( it_district ). + + SELECT p~district, p~waste_type, r~price_per_kg AS price, + SUM( p~weight_kg ) AS weight + FROM {{p}}pickup AS p + INNER JOIN {{p}}price AS r ON r~waste_type = p~waste_type + WHERE p~pickup_date BETWEEN @iv_from AND @iv_to + AND p~district IN @lt_district + GROUP BY p~district, p~waste_type, r~price_per_kg + INTO CORRESPONDING FIELDS OF TABLE @lt_raw. + + rt_lines = VALUE #( FOR ls_raw IN lt_raw + ( district = ls_raw-district + waste_type = ls_raw-waste_type + weight = ls_raw-weight + cost = round( val = ls_raw-weight / ls_raw-price dec = 2 ) ) ). + + SORT rt_lines BY cost DESCENDING district waste_type. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_costs( + iv_from = p_from iv_to = p_to it_district = s_dist[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS costs_per_group FOR TESTING. + METHODS date_range FOR TESTING. + METHODS district_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}PICKUP' ) ( '{{P}}PRICE' ) ) ). + DATA lt_price TYPE STANDARD TABLE OF {{p}}price WITH EMPTY KEY. + lt_price = VALUE #( ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) ). + go_env->insert_test_data( lt_price ). + DATA lt_pickup TYPE STANDARD TABLE OF {{p}}pickup WITH EMPTY KEY. + lt_pickup = VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) ). + go_env->insert_test_data( lt_pickup ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD costs_per_group. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_lines[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}price-price_per_kg( '30.00' ) act = lt_lines[ 1 ]-cost ). + ENDMETHOD. + + METHOD date_range. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260201' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '50.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. + + METHOD district_filter. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'S' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '300.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/faulty/m3_waste_cost.prog.abap b/tasks_gen/eval/G0112/faulty/m3_waste_cost.prog.abap new file mode 100644 index 0000000..b130110 --- /dev/null +++ b/tasks_gen/eval/G0112/faulty/m3_waste_cost.prog.abap @@ -0,0 +1,143 @@ +REPORT {{p}}waste_cost. + +DATA gv_district TYPE {{p}}pickup-district. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_dist FOR gv_district. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + weight TYPE {{p}}pickup-weight_kg, + cost TYPE {{p}}price-price_per_kg, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_district TYPE RANGE OF {{p}}pickup-district. + METHODS select_costs + IMPORTING iv_from TYPE d + iv_to TYPE d + it_district TYPE tt_district + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_raw, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + price TYPE {{p}}price-price_per_kg, + weight TYPE {{p}}pickup-weight_kg, + END OF ty_raw, + tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY. + METHODS district_range + IMPORTING it_district TYPE tt_district + RETURNING VALUE(rt_district) TYPE tt_district. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD district_range. + rt_district = it_district. + IF rt_district IS INITIAL. + rt_district = VALUE #( ( sign = 'I' option = 'CP' low = '*' ) ). + ENDIF. + ENDMETHOD. + + METHOD select_costs. + DATA lt_raw TYPE tt_raw. + DATA(lt_district) = district_range( it_district ). + + SELECT p~district, p~waste_type, r~price_per_kg AS price, + SUM( p~weight_kg ) AS weight + FROM {{p}}pickup AS p + INNER JOIN {{p}}price AS r ON r~waste_type = p~waste_type + WHERE p~pickup_date BETWEEN @iv_from AND @iv_to + AND p~district IN @lt_district + GROUP BY p~district, p~waste_type, r~price_per_kg + INTO CORRESPONDING FIELDS OF TABLE @lt_raw. + + rt_lines = VALUE #( FOR ls_raw IN lt_raw + ( district = ls_raw-district + waste_type = ls_raw-waste_type + weight = ls_raw-weight + cost = round( val = ls_raw-weight * ls_raw-price dec = 3 ) ) ). + + SORT rt_lines BY cost DESCENDING district waste_type. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_costs( + iv_from = p_from iv_to = p_to it_district = s_dist[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS costs_per_group FOR TESTING. + METHODS date_range FOR TESTING. + METHODS district_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}PICKUP' ) ( '{{P}}PRICE' ) ) ). + DATA lt_price TYPE STANDARD TABLE OF {{p}}price WITH EMPTY KEY. + lt_price = VALUE #( ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) ). + go_env->insert_test_data( lt_price ). + DATA lt_pickup TYPE STANDARD TABLE OF {{p}}pickup WITH EMPTY KEY. + lt_pickup = VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) ). + go_env->insert_test_data( lt_pickup ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD costs_per_group. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_lines[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}price-price_per_kg( '30.00' ) act = lt_lines[ 1 ]-cost ). + ENDMETHOD. + + METHOD date_range. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260201' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '50.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. + + METHOD district_filter. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'S' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '300.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/generation.json b/tasks_gen/eval/G0112/generation.json new file mode 100644 index 0000000..717ab7b --- /dev/null +++ b/tasks_gen/eval/G0112/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G0112", + "pool": "eval", + "object_type": "PROG", + "category": "B", + "attempts": [ + { + "stage": "validate", + "oracle": 90.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 3, + "killed": 2, + "ok": false + } + }, + { + "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/eval/G0112/hidden/waste_hidden.clas.abap b/tasks_gen/eval/G0112/hidden/waste_hidden.clas.abap new file mode 100644 index 0000000..6b833d5 --- /dev/null +++ b/tasks_gen/eval/G0112/hidden/waste_hidden.clas.abap @@ -0,0 +1,160 @@ +CLASS {{p}}waste_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_district TYPE RANGE OF {{p}}pickup-district. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_district TYPE tt_district OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS all_lines FOR TESTING. + METHODS weights_and_costs FOR TESTING. + METHODS date_range FOR TESTING. + METHODS district_filter FOR TESTING. + METHODS sorted_by_cost FOR TESTING. + METHODS tie_break_order FOR TESTING. + METHODS cost_rounding FOR TESTING. + METHODS double_rounding FOR TESTING. + METHODS empty_result FOR TESTING. +ENDCLASS. + + +CLASS {{p}}waste_hidden IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}PICKUP' ) ( '{{P}}PRICE' ) ) ). + DATA lt_price TYPE STANDARD TABLE OF {{p}}price WITH EMPTY KEY. + lt_price = VALUE #( + ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) + ( waste_type = 'METAL' price_per_kg = '0.50' ) + ( waste_type = 'PLASTIC' price_per_kg = '0.10' ) ). + go_env->insert_test_data( lt_price ). + DATA lt_pickup TYPE STANDARD TABLE OF {{p}}pickup WITH EMPTY KEY. + lt_pickup = VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) + ( pickup_id = 5 district = 'S' waste_type = 'METAL' pickup_date = '20251231' weight_kg = '500.00' ) + ( pickup_id = 6 district = 'S' waste_type = 'METAL' pickup_date = '20260301' weight_kg = '20.00' ) + ( pickup_id = 7 district = 'W' waste_type = 'PLASTIC' pickup_date = '20260105' weight_kg = '10.05' ) + ( pickup_id = 8 district = 'E' waste_type = 'GLASS' pickup_date = '20260112' weight_kg = '20.09' ) ). + go_env->insert_test_data( lt_pickup ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}waste_cost + WITH p_from = iv_from + WITH p_to = iv_to + WITH s_dist IN it_district + AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_lines. + cl_abap_unit_assert=>assert_equals( exp = 6 act = lines_of( run( iv_from = '20260101' iv_to = '20261231' ) ) ). + ENDMETHOD. + + METHOD weights_and_costs. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = `S` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTRICT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `PAPER` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WASTE_TYPE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `300.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WEIGHT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `30.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `150.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `WEIGHT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `15.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD date_range. + DATA(lr_data) = run( iv_from = '20260201' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `S` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTRICT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `10.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `5.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD district_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'N' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `N` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTRICT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `15.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `10.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD sorted_by_cost. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = `30.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `15.00` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `10.00` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `10.00` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.01` act = value_of( ir_data = lr_data iv_row = 5 iv_col = `COST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.00` act = value_of( ir_data = lr_data iv_row = 6 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD tie_break_order. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' ). + cl_abap_unit_assert=>assert_equals( exp = `N` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DISTRICT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `GLASS` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `WASTE_TYPE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `S` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `DISTRICT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `METAL` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `WASTE_TYPE` ) ). + ENDMETHOD. + + METHOD cost_rounding. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'W' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.01` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD double_rounding. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'E' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `GLASS` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WASTE_TYPE` ) ). + cl_abap_unit_assert=>assert_equals( exp = `20.09` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `WEIGHT` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1.00` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COST` ) ). + ENDMETHOD. + + METHOD empty_result. + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines_of( run( iv_from = '20270101' iv_to = '20271231' ) ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/mutation.json b/tasks_gen/eval/G0112/mutation.json new file mode 100644 index 0000000..ad39c5b --- /dev/null +++ b/tasks_gen/eval/G0112/mutation.json @@ -0,0 +1,57 @@ +{ + "task": "G0112", + "mutants": [ + { + "object": "{{P}}WASTE_COST", + "mutant": "line 57: AND -> OR (logic)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}WASTE_COST", + "mutant": "line 58: AND -> OR (logic)", + "status": "killed", + "hidden": "1/9", + "failed_tests": [ + "COST_ROUNDING", + "DATE_RANGE", + "DISTRICT_FILTER", + "DOUBLE_ROUNDING", + "EMPTY_RESULT", + "SORTED_BY_COST", + "TIE_BREAK_ORDER", + "WEIGHTS_AND_COSTS" + ] + }, + { + "object": "{{P}}WASTE_COST", + "mutant": "line 66: * -> / (arith)", + "status": "killed", + "hidden": "2/9", + "failed_tests": [ + "COST_ROUNDING", + "DATE_RANGE", + "DISTRICT_FILTER", + "DOUBLE_ROUNDING", + "SORTED_BY_COST", + "TIE_BREAK_ORDER", + "WEIGHTS_AND_COSTS" + ] + }, + { + "object": "{{P}}WASTE_COST", + "mutant": "line 66: 2 -> 3 (const)", + "status": "killed", + "hidden": "7/9", + "failed_tests": [ + "DOUBLE_ROUNDING", + "SORTED_BY_COST" + ] + } + ], + "valid": 3, + "killed": 3, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0112/reference/waste_cost.prog.abap b/tasks_gen/eval/G0112/reference/waste_cost.prog.abap new file mode 100644 index 0000000..357de0d --- /dev/null +++ b/tasks_gen/eval/G0112/reference/waste_cost.prog.abap @@ -0,0 +1,143 @@ +REPORT {{p}}waste_cost. + +DATA gv_district TYPE {{p}}pickup-district. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_dist FOR gv_district. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + weight TYPE {{p}}pickup-weight_kg, + cost TYPE {{p}}price-price_per_kg, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_district TYPE RANGE OF {{p}}pickup-district. + METHODS select_costs + IMPORTING iv_from TYPE d + iv_to TYPE d + it_district TYPE tt_district + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_raw, + district TYPE {{p}}pickup-district, + waste_type TYPE {{p}}pickup-waste_type, + price TYPE {{p}}price-price_per_kg, + weight TYPE {{p}}pickup-weight_kg, + END OF ty_raw, + tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY. + METHODS district_range + IMPORTING it_district TYPE tt_district + RETURNING VALUE(rt_district) TYPE tt_district. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD district_range. + rt_district = it_district. + IF rt_district IS INITIAL. + rt_district = VALUE #( ( sign = 'I' option = 'CP' low = '*' ) ). + ENDIF. + ENDMETHOD. + + METHOD select_costs. + DATA lt_raw TYPE tt_raw. + DATA(lt_district) = district_range( it_district ). + + SELECT p~district, p~waste_type, r~price_per_kg AS price, + SUM( p~weight_kg ) AS weight + FROM {{p}}pickup AS p + INNER JOIN {{p}}price AS r ON r~waste_type = p~waste_type + WHERE p~pickup_date BETWEEN @iv_from AND @iv_to + AND p~district IN @lt_district + GROUP BY p~district, p~waste_type, r~price_per_kg + INTO CORRESPONDING FIELDS OF TABLE @lt_raw. + + rt_lines = VALUE #( FOR ls_raw IN lt_raw + ( district = ls_raw-district + waste_type = ls_raw-waste_type + weight = ls_raw-weight + cost = round( val = ls_raw-weight * ls_raw-price dec = 2 ) ) ). + + SORT rt_lines BY cost DESCENDING district waste_type. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_costs( + iv_from = p_from iv_to = p_to it_district = s_dist[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS costs_per_group FOR TESTING. + METHODS date_range FOR TESTING. + METHODS district_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}PICKUP' ) ( '{{P}}PRICE' ) ) ). + DATA lt_price TYPE STANDARD TABLE OF {{p}}price WITH EMPTY KEY. + lt_price = VALUE #( ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) ). + go_env->insert_test_data( lt_price ). + DATA lt_pickup TYPE STANDARD TABLE OF {{p}}pickup WITH EMPTY KEY. + lt_pickup = VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) ). + go_env->insert_test_data( lt_pickup ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD costs_per_group. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'S' act = lt_lines[ 1 ]-district ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}price-price_per_kg( '30.00' ) act = lt_lines[ 1 ]-cost ). + ENDMETHOD. + + METHOD date_range. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260201' iv_to = '20261231' + it_district = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '50.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. + + METHOD district_filter. + DATA(lt_lines) = NEW lcl_report( )->select_costs( + iv_from = '20260101' iv_to = '20261231' + it_district = VALUE #( ( sign = 'I' option = 'EQ' low = 'S' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}pickup-weight_kg( '300.00' ) act = lt_lines[ 1 ]-weight ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/seed/pickup.tabl.asabap b/tasks_gen/eval/G0112/seed/pickup.tabl.asabap new file mode 100644 index 0000000..fefb8c8 --- /dev/null +++ b/tasks_gen/eval/G0112/seed/pickup.tabl.asabap @@ -0,0 +1,13 @@ +@EndUserText.label : 'Waste pickups' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}pickup { + key client : abap.clnt not null; + key pickup_id : abap.int4 not null; + district : abap.char(4); + waste_type : abap.char(10); + pickup_date : abap.dats; + weight_kg : abap.dec(15,2); +} diff --git a/tasks_gen/eval/G0112/seed/price.tabl.asabap b/tasks_gen/eval/G0112/seed/price.tabl.asabap new file mode 100644 index 0000000..b035727 --- /dev/null +++ b/tasks_gen/eval/G0112/seed/price.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Waste type price' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}price { + key client : abap.clnt not null; + key waste_type : abap.char(10) not null; + price_per_kg : abap.dec(15,2); +} diff --git a/tasks_gen/eval/G0112/seed/waste_seed.clas.abap b/tasks_gen/eval/G0112/seed/waste_seed.clas.abap new file mode 100644 index 0000000..2a11693 --- /dev/null +++ b/tasks_gen/eval/G0112/seed/waste_seed.clas.abap @@ -0,0 +1,26 @@ +CLASS {{p}}waste_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}waste_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}pickup. + DELETE FROM {{p}}price. + INSERT {{p}}price FROM TABLE @( VALUE #( + ( waste_type = 'PAPER' price_per_kg = '0.10' ) + ( waste_type = 'GLASS' price_per_kg = '0.05' ) + ( waste_type = 'METAL' price_per_kg = '0.50' ) + ( waste_type = 'PLASTIC' price_per_kg = '0.10' ) ) ). + INSERT {{p}}pickup FROM TABLE @( VALUE #( + ( pickup_id = 1 district = 'N' waste_type = 'PAPER' pickup_date = '20260110' weight_kg = '100.00' ) + ( pickup_id = 2 district = 'N' waste_type = 'PAPER' pickup_date = '20260210' weight_kg = '50.00' ) + ( pickup_id = 3 district = 'N' waste_type = 'GLASS' pickup_date = '20260115' weight_kg = '200.00' ) + ( pickup_id = 4 district = 'S' waste_type = 'PAPER' pickup_date = '20260120' weight_kg = '300.00' ) + ( pickup_id = 5 district = 'S' waste_type = 'METAL' pickup_date = '20251231' weight_kg = '500.00' ) + ( pickup_id = 6 district = 'S' waste_type = 'METAL' pickup_date = '20260301' weight_kg = '20.00' ) + ( pickup_id = 7 district = 'W' waste_type = 'PLASTIC' pickup_date = '20260105' weight_kg = '10.05' ) + ( pickup_id = 8 district = 'E' waste_type = 'GLASS' pickup_date = '20260112' weight_kg = '20.09' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0112/spec.md b/tasks_gen/eval/G0112/spec.md new file mode 100644 index 0000000..4037c81 --- /dev/null +++ b/tasks_gen/eval/G0112/spec.md @@ -0,0 +1,55 @@ +# 1. Goal +Show the waste collection cost for each district and waste type in an ALV list. +The waste team uses the list to compare the costs of a period. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}PICKUP exists in package $TMP. It contains data. + Fields: PICKUP_ID (INT4, key), DISTRICT (CHAR 4), WASTE_TYPE (CHAR 10), + PICKUP_DATE (DATS), WEIGHT_KG (DEC 15,2). +- Table {{P}}PRICE exists in package $TMP. It contains data. + Fields: WASTE_TYPE (CHAR 10, key), PRICE_PER_KG (DEC 15,2). + +# 4. Contract +- Create the executable program {{P}}WASTE_COST in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first pickup date to include. + - Parameter P_TO, type DATS, mandatory: the last pickup date to include. + - Select-option S_DIST for the district. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names and these types: + - DISTRICT, type {{P}}PICKUP-DISTRICT. + - WASTE_TYPE, type {{P}}PICKUP-WASTE_TYPE. + - WEIGHT, type {{P}}PICKUP-WEIGHT_KG. + - COST, type {{P}}PRICE-PRICE_PER_KG. + +# 5. Business rules +1. Include a pickup only if PICKUP_DATE is on or after P_FROM and on or before + P_TO. +2. Include a pickup only if its DISTRICT is in S_DIST. If S_DIST is empty, + include every district. +3. The cost of a pickup is WEIGHT_KG multiplied by the PRICE_PER_KG of its + waste type. Each waste type in {{P}}PICKUP has exactly one row in + {{P}}PRICE. +4. Show one line for each combination of DISTRICT and WASTE_TYPE that has at + least one included pickup. +5. WEIGHT is the sum of the WEIGHT_KG values of the included pickups of the + line. +6. COST is the sum of the costs of the included pickups of the line. Round + COST to 2 decimal places. Round half up. +7. Sort the lines by COST, the highest cost first. If two lines have the same + COST, sort them by DISTRICT in ascending order and then by WASTE_TYPE in + ascending order. +8. If no pickup is included, the list is empty. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Read the data with one database access. +- Out of scope: do not change {{P}}PICKUP and {{P}}PRICE. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/eval/G0112/task.json b/tasks_gen/eval/G0112/task.json new file mode 100644 index 0000000..2babb55 --- /dev/null +++ b/tasks_gen/eval/G0112/task.json @@ -0,0 +1,68 @@ +{ + "id": "G0112", + "category": "B", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}PICKUP", + "file": "seed/pickup.tabl.asabap", + "description": "Waste pickups per district and waste type" + }, + { + "type": "TABL", + "name": "{{P}}PRICE", + "file": "seed/price.tabl.asabap", + "description": "Price per kilogram for each waste type" + }, + { + "type": "CLAS", + "name": "{{P}}WASTE_SEED", + "file": "seed/waste_seed.clas.abap", + "description": "Waste seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}WASTE_COST", + "parameters": [ + "p_from", + "p_to", + "s_dist" + ] + } + ], + "out_of_scope": [ + "{{P}}PICKUP", + "{{P}}PRICE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}WASTE_HIDDEN", + "file": "hidden/waste_hidden.clas.abap", + "description": "Waste cost hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}WASTE_COST", + "file": "reference/waste_cost.prog.abap", + "description": "Waste collection cost report" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0113/faulty/m0_sample_batcher.clas.abap b/tasks_gen/eval/G0113/faulty/m0_sample_batcher.clas.abap new file mode 100644 index 0000000..2874355 --- /dev/null +++ b/tasks_gen/eval/G0113/faulty/m0_sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) <> abap_false + OR line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_true. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + OR iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total + 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/faulty/m1_sample_batcher.clas.abap b/tasks_gen/eval/G0113/faulty/m1_sample_batcher.clas.abap new file mode 100644 index 0000000..d29e768 --- /dev/null +++ b/tasks_gen/eval/G0113/faulty/m1_sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) = abap_false + AND line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_true. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + OR iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total + 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/faulty/m2_sample_batcher.clas.abap b/tasks_gen/eval/G0113/faulty/m2_sample_batcher.clas.abap new file mode 100644 index 0000000..ae0f402 --- /dev/null +++ b/tasks_gen/eval/G0113/faulty/m2_sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) = abap_false + OR line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_false. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + OR iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total + 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/faulty/m3_sample_batcher.clas.abap b/tasks_gen/eval/G0113/faulty/m3_sample_batcher.clas.abap new file mode 100644 index 0000000..f6dd833 --- /dev/null +++ b/tasks_gen/eval/G0113/faulty/m3_sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) = abap_false + OR line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_true. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + AND iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total + 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/faulty/m4_sample_batcher.clas.abap b/tasks_gen/eval/G0113/faulty/m4_sample_batcher.clas.abap new file mode 100644 index 0000000..4f91b8a --- /dev/null +++ b/tasks_gen/eval/G0113/faulty/m4_sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) = abap_false + OR line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_true. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + OR iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total - 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/generation.json b/tasks_gen/eval/G0113/generation.json new file mode 100644 index 0000000..884eff9 --- /dev/null +++ b/tasks_gen/eval/G0113/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0113", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 67.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/eval/G0113/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0113/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..20ce2f3 --- /dev/null +++ b/tasks_gen/eval/G0113/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. + DATA mo_cut TYPE REF TO {{p}}sample_batcher. + + METHODS setup. + METHODS adds_sample FOR TESTING. + METHODS counts_urgent FOR TESTING. + METHODS rejects_empty_id FOR TESTING. + METHODS rejects_empty_type FOR TESTING. + METHODS rejects_duplicate FOR TESTING. + METHODS rejects_bad_priority FOR TESTING. + METHODS normalizes_input FOR TESTING. + METHODS batches_sorted FOR TESTING. + METHODS one_row_per_type FOR TESTING. + METHODS get_batch_known FOR TESTING. + METHODS get_batch_unknown FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}sample_batcher( ). + ENDMETHOD. + + METHOD adds_sample. + cl_abap_unit_assert=>assert_equals( + exp = abap_true + act = mo_cut->add_sample( iv_sample_id = 'S0000001' + iv_test_type = 'GLU' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_batches ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_batches[ 1 ]-total ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_batches[ 1 ]-urgent ). + ENDMETHOD. + + METHOD counts_urgent. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'K' iv_priority = 1 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'K' iv_priority = 3 ). + mo_cut->add_sample( iv_sample_id = 'S3' iv_test_type = 'K' iv_priority = 1 ). + DATA(ls_batch) = mo_cut->get_batch( 'K' ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_batch-total ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_batch-urgent ). + ENDMETHOD. + + METHOD rejects_empty_id. + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = '' + iv_test_type = 'GLU' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_batches ) ). + ENDMETHOD. + + METHOD rejects_empty_type. + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 'S1' + iv_test_type = ' ' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_batches ) ). + ENDMETHOD. + + METHOD rejects_duplicate. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'GLU' iv_priority = 2 ). + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 's1' + iv_test_type = 'NA' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_batches ) ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = lt_batches[ 1 ]-test_type ). + ENDMETHOD. + + METHOD rejects_bad_priority. + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 'S1' + iv_test_type = 'GLU' + iv_priority = 4 ) ). + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 'S2' + iv_test_type = 'GLU' + iv_priority = 0 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_batches ) ). + ENDMETHOD. + + METHOD normalizes_input. + cl_abap_unit_assert=>assert_equals( + exp = abap_true + act = mo_cut->add_sample( iv_sample_id = ' s0000001 ' + iv_test_type = 'glu' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_batches ) ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = lt_batches[ 1 ]-test_type ). + ENDMETHOD. + + METHOD batches_sorted. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'NA' iv_priority = 2 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'GLU' iv_priority = 2 ). + mo_cut->add_sample( iv_sample_id = 'S3' iv_test_type = 'K' iv_priority = 2 ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_batches ) ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = lt_batches[ 1 ]-test_type ). + cl_abap_unit_assert=>assert_equals( exp = 'K' act = lt_batches[ 2 ]-test_type ). + cl_abap_unit_assert=>assert_equals( exp = 'NA' act = lt_batches[ 3 ]-test_type ). + ENDMETHOD. + + METHOD one_row_per_type. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'GLU' iv_priority = 1 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'GLU' iv_priority = 2 ). + mo_cut->add_sample( iv_sample_id = 'S3' iv_test_type = 'GLU' iv_priority = 3 ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_batches ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_batches[ 1 ]-total ). + ENDMETHOD. + + METHOD get_batch_known. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'GLU' iv_priority = 1 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'NA' iv_priority = 2 ). + DATA(ls_batch) = mo_cut->get_batch( 'GLU' ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = ls_batch-test_type ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_batch-total ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_batch-urgent ). + ENDMETHOD. + + METHOD get_batch_unknown. + DATA(ls_batch) = mo_cut->get_batch( 'XXX' ). + cl_abap_unit_assert=>assert_equals( exp = 'XXX' act = ls_batch-test_type ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_batch-total ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_batch-urgent ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/mutation.json b/tasks_gen/eval/G0113/mutation.json new file mode 100644 index 0000000..3fe9622 --- /dev/null +++ b/tasks_gen/eval/G0113/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "G0113", + "mutants": [ + { + "object": "{{P}}SAMPLE_BATCHER", + "mutant": "line 80: = -> <> (eq)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "ADDS_SAMPLE", + "BATCHES_SORTED", + "COUNTS_URGENT", + "GET_BATCH_KNOWN", + "NORMALIZES_INPUT", + "ONE_ROW_PER_TYPE", + "REJECTS_BAD_PRIORITY", + "REJECTS_DUPLICATE" + ] + }, + { + "object": "{{P}}SAMPLE_BATCHER", + "mutant": "line 81: OR -> AND (logic)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "REJECTS_BAD_PRIORITY", + "REJECTS_DUPLICATE" + ] + }, + { + "object": "{{P}}SAMPLE_BATCHER", + "mutant": "line 88: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "ADDS_SAMPLE", + "NORMALIZES_INPUT" + ] + }, + { + "object": "{{P}}SAMPLE_BATCHER", + "mutant": "line 120: OR -> AND (logic)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "ADDS_SAMPLE", + "BATCHES_SORTED", + "COUNTS_URGENT", + "NORMALIZES_INPUT", + "ONE_ROW_PER_TYPE", + "REJECTS_DUPLICATE" + ] + }, + { + "object": "{{P}}SAMPLE_BATCHER", + "mutant": "line 131: + -> - (arith)", + "status": "killed", + "hidden": "7/11", + "failed_tests": [ + "ADDS_SAMPLE", + "COUNTS_URGENT", + "GET_BATCH_KNOWN", + "ONE_ROW_PER_TYPE" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0113/reference/sample_batcher.clas.abap b/tasks_gen/eval/G0113/reference/sample_batcher.clas.abap new file mode 100644 index 0000000..dc5ac31 --- /dev/null +++ b/tasks_gen/eval/G0113/reference/sample_batcher.clas.abap @@ -0,0 +1,137 @@ +CLASS {{p}}sample_batcher DEFINITION PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + + TYPES: + ty_test_type TYPE c LENGTH 4, + ty_sample_id TYPE c LENGTH 10, + ty_priority TYPE i. + + TYPES: + BEGIN OF ty_batch, + test_type TYPE ty_test_type, + total TYPE i, + urgent TYPE i, + END OF ty_batch. + + TYPES tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY. + + METHODS add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool. + + METHODS get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches. + + METHODS get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_priority, + urgent TYPE ty_priority VALUE 1, + normal TYPE ty_priority VALUE 2, + routine TYPE ty_priority VALUE 3, + END OF c_priority. + + TYPES: + BEGIN OF ty_sample_entry, + sample_id TYPE ty_sample_id, + END OF ty_sample_entry. + + TYPES: + tt_batch_index TYPE SORTED TABLE OF ty_batch WITH UNIQUE KEY test_type, + tt_sample_index TYPE HASHED TABLE OF ty_sample_entry WITH UNIQUE KEY sample_id. + + DATA mt_batches TYPE tt_batch_index. + DATA mt_samples TYPE tt_sample_index. + + METHODS normalize_id + IMPORTING iv_id TYPE ty_sample_id + RETURNING VALUE(rv_id) TYPE ty_sample_id. + + METHODS normalize_type + IMPORTING iv_type TYPE ty_test_type + RETURNING VALUE(rv_type) TYPE ty_test_type. + + METHODS is_valid_priority + IMPORTING iv_priority TYPE ty_priority + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS count_sample + IMPORTING iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority. + +ENDCLASS. + + +CLASS {{p}}sample_batcher IMPLEMENTATION. + + METHOD add_sample. + DATA(lv_sample_id) = normalize_id( iv_sample_id ). + DATA(lv_test_type) = normalize_type( iv_test_type ). + + IF lv_sample_id IS INITIAL + OR lv_test_type IS INITIAL + OR is_valid_priority( iv_priority ) = abap_false + OR line_exists( mt_samples[ sample_id = lv_sample_id ] ). + RETURN. + ENDIF. + + INSERT VALUE #( sample_id = lv_sample_id ) INTO TABLE mt_samples. + count_sample( iv_test_type = lv_test_type + iv_priority = iv_priority ). + rv_added = abap_true. + ENDMETHOD. + + + METHOD get_batches. + rt_batches = mt_batches. + SORT rt_batches BY test_type. + ENDMETHOD. + + + METHOD get_batch. + DATA(lv_test_type) = normalize_type( iv_test_type ). + READ TABLE mt_batches INTO rs_batch WITH TABLE KEY test_type = lv_test_type. + IF sy-subrc <> 0. + rs_batch = VALUE #( test_type = lv_test_type ). + ENDIF. + ENDMETHOD. + + + METHOD normalize_id. + rv_id = to_upper( replace( val = iv_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD normalize_type. + rv_type = to_upper( replace( val = iv_type sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + + METHOD is_valid_priority. + rv_valid = xsdbool( iv_priority = c_priority-urgent + OR iv_priority = c_priority-normal + OR iv_priority = c_priority-routine ). + ENDMETHOD. + + + METHOD count_sample. + READ TABLE mt_batches ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY test_type = iv_test_type. + IF sy-subrc <> 0. + INSERT VALUE #( test_type = iv_test_type ) INTO TABLE mt_batches + ASSIGNING . + ENDIF. + -total = -total + 1. + IF iv_priority = c_priority-urgent. + -urgent = -urgent + 1. + ENDIF. + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/reference/sample_batcher.testclasses.abap b/tasks_gen/eval/G0113/reference/sample_batcher.testclasses.abap new file mode 100644 index 0000000..54c8bdc --- /dev/null +++ b/tasks_gen/eval/G0113/reference/sample_batcher.testclasses.abap @@ -0,0 +1,88 @@ +CLASS ltc_sample_batcher DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}sample_batcher. + + METHODS setup. + METHODS counts_samples FOR TESTING. + METHODS counts_urgent FOR TESTING. + METHODS rejects_empty_id FOR TESTING. + METHODS rejects_duplicate FOR TESTING. + METHODS rejects_bad_priority FOR TESTING. + METHODS normalizes_input FOR TESTING. + METHODS batches_sorted FOR TESTING. + METHODS unknown_type_is_zero FOR TESTING. +ENDCLASS. + + +CLASS ltc_sample_batcher IMPLEMENTATION. + + METHOD setup. + mo_cut = NEW {{p}}sample_batcher( ). + ENDMETHOD. + + METHOD counts_samples. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'GLU' iv_priority = 2 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'GLU' iv_priority = 3 ). + DATA(ls_batch) = mo_cut->get_batch( 'GLU' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_batch-total ). + ENDMETHOD. + + METHOD counts_urgent. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'K' iv_priority = 1 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'K' iv_priority = 2 ). + DATA(ls_batch) = mo_cut->get_batch( 'K' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_batch-total ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_batch-urgent ). + ENDMETHOD. + + METHOD rejects_empty_id. + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = '' + iv_test_type = 'GLU' + iv_priority = 2 ) ). + ENDMETHOD. + + METHOD rejects_duplicate. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'GLU' iv_priority = 2 ). + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 'S1' + iv_test_type = 'NA' + iv_priority = 2 ) ). + ENDMETHOD. + + METHOD rejects_bad_priority. + cl_abap_unit_assert=>assert_equals( + exp = abap_false + act = mo_cut->add_sample( iv_sample_id = 'S1' + iv_test_type = 'GLU' + iv_priority = 9 ) ). + ENDMETHOD. + + METHOD normalizes_input. + cl_abap_unit_assert=>assert_equals( + exp = abap_true + act = mo_cut->add_sample( iv_sample_id = 's1' + iv_test_type = 'glu' + iv_priority = 2 ) ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = lt_batches[ 1 ]-test_type ). + ENDMETHOD. + + METHOD batches_sorted. + mo_cut->add_sample( iv_sample_id = 'S1' iv_test_type = 'NA' iv_priority = 2 ). + mo_cut->add_sample( iv_sample_id = 'S2' iv_test_type = 'GLU' iv_priority = 2 ). + DATA(lt_batches) = mo_cut->get_batches( ). + cl_abap_unit_assert=>assert_equals( exp = 'GLU' act = lt_batches[ 1 ]-test_type ). + cl_abap_unit_assert=>assert_equals( exp = 'NA' act = lt_batches[ 2 ]-test_type ). + ENDMETHOD. + + METHOD unknown_type_is_zero. + DATA(ls_batch) = mo_cut->get_batch( 'XXX' ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_batch-total ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_batch-urgent ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0113/spec.md b/tasks_gen/eval/G0113/spec.md new file mode 100644 index 0000000..4c785cb --- /dev/null +++ b/tasks_gen/eval/G0113/spec.md @@ -0,0 +1,77 @@ +# 1. Goal +A laboratory receives blood samples. Each sample has a sample ID, a test type and a +priority. The laboratory groups the samples into batches. One batch belongs to each +test type. A laboratory program adds the samples one by one. Then it reads the +batches. + +Create the class {{P}}SAMPLE_BATCHER. The class collects the samples and gives the +batches. + +# 2. Open questions +None. + +# 3. Context +- The class is new. It is in package $TMP. +- No other object is necessary. +- A laboratory program creates one instance of the class. It adds all samples of a + shift. Then it reads the batches. + +# 4. Contract +Class {{P}}SAMPLE_BATCHER. The class is public and final. It can be created without +parameters. + +Public types: +- ty_test_type TYPE c LENGTH 4 +- ty_sample_id TYPE c LENGTH 10 +- ty_priority TYPE i +- ty_batch with the components test_type (TYPE ty_test_type), total (TYPE i) and + urgent (TYPE i) +- tt_batches TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY + +Public methods: +- add_sample + IMPORTING iv_sample_id TYPE ty_sample_id + iv_test_type TYPE ty_test_type + iv_priority TYPE ty_priority + RETURNING VALUE(rv_added) TYPE abap_bool +- get_batches + RETURNING VALUE(rt_batches) TYPE tt_batches +- get_batch + IMPORTING iv_test_type TYPE ty_test_type + RETURNING VALUE(rs_batch) TYPE ty_batch + +Do not add other public methods. + +# 5. Business rules + +add_sample +1. Normalize the sample ID and the test type. Remove all spaces. Convert all letters + to upper case. +2. Reject the sample and return abap_false if one of these conditions is true: + - the normalized sample ID is empty, or + - the normalized test type is empty, or + - the priority is not 1, 2 or 3, or + - the normalized sample ID was already added. + The state of the object does not change. +3. Else accept the sample and return abap_true. The batch of the test type gets one + more sample. If the priority is 1, the batch also gets one more urgent sample. + +get_batches +4. Return one row for each test type that has at least one sample. +5. Sort the rows by the test type in ascending order. + +get_batch +6. Normalize the test type in the same way as in rule 1. +7. If the test type has at least one sample, return its row. +8. Else return a row with this test type, total = 0 and urgent = 0. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Methods below 40 statements. No global variables. No + comment that restates the code. +- Out of scope: no other object is part of this task. + +# 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/eval/G0113/task.json b/tasks_gen/eval/G0113/task.json new file mode 100644 index 0000000..88bdce6 --- /dev/null +++ b/tasks_gen/eval/G0113/task.json @@ -0,0 +1,42 @@ +{ + "id": "G0113", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}SAMPLE_BATCHER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the sample batcher" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}SAMPLE_BATCHER", + "file": "reference/sample_batcher.clas.abap", + "description": "Sample batcher", + "testclasses_file": "reference/sample_batcher.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "linear_search_in_loop", + "sorted_or_hashed_table" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0114/faulty/m0_dispatch_planner.clas.abap b/tasks_gen/eval/G0114/faulty/m0_dispatch_planner.clas.abap new file mode 100644 index 0000000..5a29f3a --- /dev/null +++ b/tasks_gen/eval/G0114/faulty/m0_dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels > 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 1 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight + is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/faulty/m1_dispatch_planner.clas.abap b/tasks_gen/eval/G0114/faulty/m1_dispatch_planner.clas.abap new file mode 100644 index 0000000..0180d14 --- /dev/null +++ b/tasks_gen/eval/G0114/faulty/m1_dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels <= 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 2 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight + is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/faulty/m2_dispatch_planner.clas.abap b/tasks_gen/eval/G0114/faulty/m2_dispatch_planner.clas.abap new file mode 100644 index 0000000..bd84e58 --- /dev/null +++ b/tasks_gen/eval/G0114/faulty/m2_dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels <= 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 1 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries - ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight + is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/faulty/m3_dispatch_planner.clas.abap b/tasks_gen/eval/G0114/faulty/m3_dispatch_planner.clas.abap new file mode 100644 index 0000000..4f95aa3 --- /dev/null +++ b/tasks_gen/eval/G0114/faulty/m3_dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels <= 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 1 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries - is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight + is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/faulty/m4_dispatch_planner.clas.abap b/tasks_gen/eval/G0114/faulty/m4_dispatch_planner.clas.abap new file mode 100644 index 0000000..3695e8f --- /dev/null +++ b/tasks_gen/eval/G0114/faulty/m4_dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels <= 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 1 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight - is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/generation.json b/tasks_gen/eval/G0114/generation.json new file mode 100644 index 0000000..2d2d353 --- /dev/null +++ b/tasks_gen/eval/G0114/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G0114", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "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/eval/G0114/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0114/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..60b8c03 --- /dev/null +++ b/tasks_gen/eval/G0114/hidden/t02_hidden.clas.abap @@ -0,0 +1,238 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_id TYPE c LENGTH 10. + TYPES ty_district TYPE c LENGTH 4. + TYPES ty_weight TYPE p LENGTH 8 DECIMALS 2. + + DATA mo_cut TYPE REF TO {{p}}dispatch_planner. + + METHODS setup. + METHODS make_delivery + IMPORTING iv_id TYPE ty_id + iv_district TYPE ty_district + iv_parcels TYPE i + iv_weight TYPE ty_weight + iv_urgency TYPE i + RETURNING VALUE(rs_delivery) TYPE {{p}}dispatch_planner=>ty_delivery. + METHODS make_line + IMPORTING iv_district TYPE ty_district + iv_deliveries TYPE i + iv_parcels TYPE i + iv_weight TYPE ty_weight + iv_urgency TYPE i + RETURNING VALUE(rs_line) TYPE {{p}}dispatch_planner=>ty_district. + + METHODS empty_input FOR TESTING. + METHODS one_delivery FOR TESTING. + METHODS groups_districts FOR TESTING. + METHODS ignores_zero_parcels FOR TESTING. + METHODS sums_parcels_weight FOR TESTING. + METHODS top_urgency_is_min FOR TESTING. + METHODS result_is_sorted FOR TESTING. + METHODS merge_adds_values FOR TESTING. + METHODS merge_single_district FOR TESTING. + METHODS merge_urgency_min FOR TESTING. + METHODS merge_sorted FOR TESTING. + METHODS merge_empty FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}dispatch_planner( ). + ENDMETHOD. + + METHOD make_delivery. + rs_delivery = VALUE #( delivery_id = iv_id + district = iv_district + parcels = iv_parcels + weight = iv_weight + urgency = iv_urgency ). + ENDMETHOD. + + METHOD make_line. + rs_line = VALUE #( district = iv_district + deliveries = iv_deliveries + parcels = iv_parcels + weight = iv_weight + top_urgency = iv_urgency ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD one_delivery. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'NORD' iv_parcels = 3 + iv_weight = '10.50' iv_urgency = 2 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_line-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_line-parcels ). + DATA lv_weight TYPE ty_weight. + lv_weight = '10.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_weight act = ls_line-weight ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD groups_districts. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'NORD' iv_parcels = 2 + iv_weight = '5.00' iv_urgency = 1 ) ) + ( make_delivery( iv_id = 'D2' iv_district = 'SUED' iv_parcels = 4 + iv_weight = '7.00' iv_urgency = 3 ) ) + ( make_delivery( iv_id = 'D3' iv_district = 'NORD' iv_parcels = 1 + iv_weight = '2.00' iv_urgency = 5 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + DATA(ls_nord) = lt_result[ district = 'NORD' ]. + DATA(ls_sued) = lt_result[ district = 'SUED' ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_nord-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_sued-deliveries ). + ENDMETHOD. + + METHOD ignores_zero_parcels. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'NORD' iv_parcels = 0 + iv_weight = '5.00' iv_urgency = 1 ) ) + ( make_delivery( iv_id = 'D2' iv_district = 'OST' iv_parcels = -2 + iv_weight = '3.00' iv_urgency = 2 ) ) + ( make_delivery( iv_id = 'D3' iv_district = 'WEST' iv_parcels = 2 + iv_weight = '3.00' iv_urgency = 2 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + READ TABLE lt_result WITH KEY district = 'NORD' TRANSPORTING NO FIELDS. + cl_abap_unit_assert=>assert_subrc( exp = 4 msg = 'NORD must not be in the result' ). + READ TABLE lt_result WITH KEY district = 'OST' TRANSPORTING NO FIELDS. + cl_abap_unit_assert=>assert_subrc( exp = 4 msg = 'OST must not be in the result' ). + DATA(ls_line) = lt_result[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'WEST' act = ls_line-district ). + ENDMETHOD. + + METHOD sums_parcels_weight. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'NORD' iv_parcels = 2 + iv_weight = '10.25' iv_urgency = 1 ) ) + ( make_delivery( iv_id = 'D2' iv_district = 'NORD' iv_parcels = 3 + iv_weight = '4.75' iv_urgency = 1 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = ls_line-parcels ). + DATA lv_weight TYPE ty_weight. + lv_weight = '15.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_weight act = ls_line-weight ). + ENDMETHOD. + + METHOD top_urgency_is_min. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'NORD' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 5 ) ) + ( make_delivery( iv_id = 'D2' iv_district = 'NORD' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 2 ) ) + ( make_delivery( iv_id = 'D3' iv_district = 'NORD' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 4 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD result_is_sorted. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( make_delivery( iv_id = 'D1' iv_district = 'WEST' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) + ( make_delivery( iv_id = 'D2' iv_district = 'NORD' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) + ( make_delivery( iv_id = 'D3' iv_district = 'OST' iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + DATA(ls_first) = lt_result[ 1 ]. + DATA(ls_second) = lt_result[ 2 ]. + DATA(ls_third) = lt_result[ 3 ]. + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = ls_first-district ). + cl_abap_unit_assert=>assert_equals( exp = 'OST' act = ls_second-district ). + cl_abap_unit_assert=>assert_equals( exp = 'WEST' act = ls_third-district ). + ENDMETHOD. + + METHOD merge_adds_values. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( make_line( iv_district = 'NORD' iv_deliveries = 2 iv_parcels = 5 + iv_weight = '10.00' iv_urgency = 3 ) ) ). + lt_right = VALUE #( ( make_line( iv_district = 'NORD' iv_deliveries = 1 iv_parcels = 4 + iv_weight = '2.50' iv_urgency = 2 ) ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_line-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 9 act = ls_line-parcels ). + DATA lv_weight TYPE ty_weight. + lv_weight = '12.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_weight act = ls_line-weight ). + ENDMETHOD. + + METHOD merge_single_district. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( make_line( iv_district = 'NORD' iv_deliveries = 2 iv_parcels = 5 + iv_weight = '10.00' iv_urgency = 3 ) ) ). + lt_right = VALUE #( ( make_line( iv_district = 'SUED' iv_deliveries = 4 iv_parcels = 8 + iv_weight = '6.00' iv_urgency = 1 ) ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + DATA(ls_sued) = lt_result[ district = 'SUED' ]. + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_sued-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 8 act = ls_sued-parcels ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_sued-top_urgency ). + ENDMETHOD. + + METHOD merge_urgency_min. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( make_line( iv_district = 'NORD' iv_deliveries = 1 iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 4 ) ) ). + lt_right = VALUE #( ( make_line( iv_district = 'NORD' iv_deliveries = 1 iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 7 ) ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD merge_sorted. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( make_line( iv_district = 'WEST' iv_deliveries = 1 iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) ). + lt_right = VALUE #( ( make_line( iv_district = 'OST' iv_deliveries = 1 iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) + ( make_line( iv_district = 'NORD' iv_deliveries = 1 iv_parcels = 1 + iv_weight = '1.00' iv_urgency = 1 ) ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + DATA(ls_first) = lt_result[ 1 ]. + DATA(ls_second) = lt_result[ 2 ]. + DATA(ls_third) = lt_result[ 3 ]. + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = ls_first-district ). + cl_abap_unit_assert=>assert_equals( exp = 'OST' act = ls_second-district ). + cl_abap_unit_assert=>assert_equals( exp = 'WEST' act = ls_third-district ). + ENDMETHOD. + + METHOD merge_empty. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/mutation.json b/tasks_gen/eval/G0114/mutation.json new file mode 100644 index 0000000..5acba81 --- /dev/null +++ b/tasks_gen/eval/G0114/mutation.json @@ -0,0 +1,62 @@ +{ + "task": "G0114", + "mutants": [ + { + "object": "{{P}}DISPATCH_PLANNER", + "mutant": "line 72: <= -> > (rel)", + "status": "killed", + "hidden": "6/12", + "failed_tests": [ + "GROUPS_DISTRICTS", + "IGNORES_ZERO_PARCELS", + "ONE_DELIVERY", + "RESULT_IS_SORTED", + "SUMS_PARCELS_WEIGHT", + "TOP_URGENCY_IS_MIN" + ] + }, + { + "object": "{{P}}DISPATCH_PLANNER", + "mutant": "line 76: 1 -> 2 (const)", + "status": "killed", + "hidden": "9/12", + "failed_tests": [ + "GROUPS_DISTRICTS", + "ONE_DELIVERY", + "SUMS_PARCELS_WEIGHT" + ] + }, + { + "object": "{{P}}DISPATCH_PLANNER", + "mutant": "line 86: + -> - (arith)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "GROUPS_DISTRICTS", + "SUMS_PARCELS_WEIGHT" + ] + }, + { + "object": "{{P}}DISPATCH_PLANNER", + "mutant": "line 100: + -> - (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "MERGE_ADDS_VALUES" + ] + }, + { + "object": "{{P}}DISPATCH_PLANNER", + "mutant": "line 102: + -> - (arith)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "MERGE_ADDS_VALUES" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0114/reference/dispatch_planner.clas.abap b/tasks_gen/eval/G0114/reference/dispatch_planner.clas.abap new file mode 100644 index 0000000..28d4ae3 --- /dev/null +++ b/tasks_gen/eval/G0114/reference/dispatch_planner.clas.abap @@ -0,0 +1,110 @@ +CLASS {{p}}dispatch_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_delivery, + delivery_id TYPE c LENGTH 10, + district TYPE c LENGTH 4, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + urgency TYPE i, + END OF ty_delivery, + tt_delivery TYPE STANDARD TABLE OF ty_delivery WITH EMPTY KEY, + BEGIN OF ty_district, + district TYPE c LENGTH 4, + deliveries TYPE i, + parcels TYPE i, + weight TYPE p LENGTH 8 DECIMALS 2, + top_urgency TYPE i, + END OF ty_district, + tt_district TYPE STANDARD TABLE OF ty_district WITH EMPTY KEY. + + METHODS constructor. + METHODS plan + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. + METHODS merge + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + + PRIVATE SECTION. + TYPES tt_group TYPE SORTED TABLE OF ty_district WITH UNIQUE KEY district. + + METHODS add_delivery + IMPORTING is_delivery TYPE ty_delivery + CHANGING ct_group TYPE tt_group. + METHODS add_line + IMPORTING is_line TYPE ty_district + CHANGING ct_group TYPE tt_group. + METHODS to_result + IMPORTING it_group TYPE tt_group + RETURNING VALUE(rt_result) TYPE tt_district. +ENDCLASS. + + +CLASS {{p}}dispatch_planner IMPLEMENTATION. + METHOD constructor. + ENDMETHOD. + + METHOD plan. + DATA lt_group TYPE tt_group. + LOOP AT it_delivery INTO DATA(ls_delivery). + add_delivery( EXPORTING is_delivery = ls_delivery + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_district = to_result( lt_group ). + ENDMETHOD. + + METHOD merge. + DATA lt_group TYPE tt_group. + LOOP AT it_left INTO DATA(ls_left). + add_line( EXPORTING is_line = ls_left + CHANGING ct_group = lt_group ). + ENDLOOP. + LOOP AT it_right INTO DATA(ls_right). + add_line( EXPORTING is_line = ls_right + CHANGING ct_group = lt_group ). + ENDLOOP. + rt_merged = to_result( lt_group ). + ENDMETHOD. + + METHOD add_delivery. + IF is_delivery-parcels <= 0. + RETURN. + ENDIF. + DATA(ls_new) = VALUE ty_district( district = is_delivery-district + deliveries = 1 + parcels = is_delivery-parcels + weight = is_delivery-weight + top_urgency = is_delivery-urgency ). + READ TABLE ct_group WITH TABLE KEY district = is_delivery-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT ls_new INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + ls_new-deliveries. + -parcels = -parcels + ls_new-parcels. + -weight = -weight + ls_new-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = ls_new-top_urgency ). + ENDMETHOD. + + METHOD add_line. + READ TABLE ct_group WITH TABLE KEY district = is_line-district + ASSIGNING FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT is_line INTO TABLE ct_group. + RETURN. + ENDIF. + -deliveries = -deliveries + is_line-deliveries. + -parcels = -parcels + is_line-parcels. + -weight = -weight + is_line-weight. + -top_urgency = nmin( val1 = -top_urgency + val2 = is_line-top_urgency ). + ENDMETHOD. + + METHOD to_result. + rt_result = it_group. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/reference/dispatch_planner.testclasses.abap b/tasks_gen/eval/G0114/reference/dispatch_planner.testclasses.abap new file mode 100644 index 0000000..24e0e8c --- /dev/null +++ b/tasks_gen/eval/G0114/reference/dispatch_planner.testclasses.abap @@ -0,0 +1,126 @@ +CLASS ltc_dispatch_planner DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_weight TYPE p LENGTH 8 DECIMALS 2. + + DATA mo_cut TYPE REF TO {{p}}dispatch_planner. + + METHODS setup. + METHODS plan_groups_districts FOR TESTING. + METHODS plan_ignores_zero FOR TESTING. + METHODS plan_sums_values FOR TESTING. + METHODS plan_sorts_result FOR TESTING. + METHODS plan_empty_input FOR TESTING. + METHODS merge_adds_values FOR TESTING. + METHODS merge_keeps_single FOR TESTING. + METHODS merge_urgency_min FOR TESTING. + METHODS merge_empty FOR TESTING. +ENDCLASS. + + +CLASS ltc_dispatch_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}dispatch_planner( ). + ENDMETHOD. + + METHOD plan_groups_districts. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( delivery_id = 'D1' district = 'NORD' parcels = 2 weight = '5.00' urgency = 1 ) + ( delivery_id = 'D2' district = 'SUED' parcels = 4 weight = '7.00' urgency = 3 ) + ( delivery_id = 'D3' district = 'NORD' parcels = 1 weight = '2.00' urgency = 5 ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + DATA(ls_nord) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_nord-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_nord-parcels ). + ENDMETHOD. + + METHOD plan_ignores_zero. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( delivery_id = 'D1' district = 'NORD' parcels = 0 weight = '5.00' urgency = 1 ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD plan_sums_values. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( delivery_id = 'D1' district = 'NORD' parcels = 2 weight = '10.25' urgency = 1 ) + ( delivery_id = 'D2' district = 'NORD' parcels = 3 weight = '4.75' urgency = 4 ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 5 act = ls_line-parcels ). + DATA lv_weight TYPE ty_weight. + lv_weight = '15.00'. + cl_abap_unit_assert=>assert_equals( exp = lv_weight act = ls_line-weight ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD plan_sorts_result. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + lt_delivery = VALUE #( + ( delivery_id = 'D1' district = 'WEST' parcels = 1 weight = '1.00' urgency = 1 ) + ( delivery_id = 'D2' district = 'NORD' parcels = 1 weight = '1.00' urgency = 1 ) ). + DATA(lt_result) = mo_cut->plan( lt_delivery ). + DATA(ls_first) = lt_result[ 1 ]. + cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = ls_first-district ). + ENDMETHOD. + + METHOD plan_empty_input. + DATA lt_delivery TYPE {{p}}dispatch_planner=>tt_delivery. + DATA(lt_result) = mo_cut->plan( lt_delivery ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD merge_adds_values. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( district = 'NORD' deliveries = 2 parcels = 5 + weight = '10.00' top_urgency = 3 ) ). + lt_right = VALUE #( ( district = 'NORD' deliveries = 1 parcels = 4 + weight = '2.50' top_urgency = 2 ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 3 act = ls_line-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 9 act = ls_line-parcels ). + DATA lv_weight TYPE ty_weight. + lv_weight = '12.50'. + cl_abap_unit_assert=>assert_equals( exp = lv_weight act = ls_line-weight ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD merge_keeps_single. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( district = 'NORD' deliveries = 2 parcels = 5 + weight = '10.00' top_urgency = 3 ) ). + lt_right = VALUE #( ( district = 'SUED' deliveries = 4 parcels = 8 + weight = '6.00' top_urgency = 1 ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + DATA(ls_sued) = lt_result[ district = 'SUED' ]. + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_sued-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_sued-top_urgency ). + ENDMETHOD. + + METHOD merge_urgency_min. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + lt_left = VALUE #( ( district = 'NORD' deliveries = 1 parcels = 1 + weight = '1.00' top_urgency = 4 ) ). + lt_right = VALUE #( ( district = 'NORD' deliveries = 1 parcels = 1 + weight = '1.00' top_urgency = 7 ) ). + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + DATA(ls_line) = lt_result[ district = 'NORD' ]. + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_line-top_urgency ). + ENDMETHOD. + + METHOD merge_empty. + DATA lt_left TYPE {{p}}dispatch_planner=>tt_district. + DATA lt_right TYPE {{p}}dispatch_planner=>tt_district. + DATA(lt_result) = mo_cut->merge( it_left = lt_left it_right = lt_right ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0114/spec.md b/tasks_gen/eval/G0114/spec.md new file mode 100644 index 0000000..7870318 --- /dev/null +++ b/tasks_gen/eval/G0114/spec.md @@ -0,0 +1,68 @@ +# 1. Goal +A courier company dispatches deliveries. Each delivery belongs to a district. The +dispatcher needs one service that groups the deliveries per district, and one +service that merges two district plans. A planning program calls both services. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}DISPATCH_PLANNER does not exist. Create it in package $TMP. +- A delivery has an ID, a district code, a number of parcels, a weight and an + urgency. A small urgency value means a high urgency. +- A district plan has one line per district. +- The planning program reads the deliveries of one day and calls PLAN. +- A second depot sends its own plan. The dispatcher calls MERGE with the plan of + the first depot and the plan of the second depot. + +# 4. Contract +- Create the class {{P}}DISPATCH_PLANNER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has the public methods PLAN and MERGE. It has no other public method. +- Public types of the class: + - TY_DELIVERY with the components DELIVERY_ID (character 10), DISTRICT + (character 4), PARCELS (integer), WEIGHT (packed number, length 8, 2 + decimals) and URGENCY (integer). + - TT_DELIVERY: a standard table of TY_DELIVERY with an empty key. + - TY_DISTRICT with the components DISTRICT (character 4), DELIVERIES + (integer), PARCELS (integer), WEIGHT (packed number, length 8, 2 decimals) + and TOP_URGENCY (integer). + - TT_DISTRICT: a standard table of TY_DISTRICT with an empty key. +- Method PLAN: + IMPORTING it_delivery TYPE tt_delivery + RETURNING VALUE(rt_district) TYPE tt_district. +- Method MERGE: + IMPORTING it_left TYPE tt_district + it_right TYPE tt_district + RETURNING VALUE(rt_merged) TYPE tt_district. + +# 5. Business rules +Method PLAN: +1. Ignore each delivery whose PARCELS value is zero or negative. +2. Group the other deliveries by DISTRICT. Compare the district codes exactly. +3. Each district has exactly one line in the result: + - DELIVERIES: the number of deliveries of the district. + - PARCELS: the sum of the parcels of the deliveries of the district. + - WEIGHT: the sum of the weights of the deliveries of the district. + - TOP_URGENCY: the smallest urgency of the deliveries of the district. +4. Sort the result by DISTRICT in ascending order. +5. If no delivery remains, the result is empty. + +Method MERGE: +6. A district that occurs in both plans has exactly one line in the result: + - DELIVERIES, PARCELS and WEIGHT: the sum of the two values. + - TOP_URGENCY: the smaller of the two values. +7. A district that occurs in only one plan keeps its values. +8. Sort the result by DISTRICT in ascending order. +9. If both plans are empty, the result is empty. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: no other global object in package $TMP. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/eval/G0114/task.json b/tasks_gen/eval/G0114/task.json new file mode 100644 index 0000000..a81d00f --- /dev/null +++ b/tasks_gen/eval/G0114/task.json @@ -0,0 +1,42 @@ +{ + "id": "G0114", + "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}}DISPATCH_PLANNER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}DISPATCH_PLANNER", + "file": "reference/dispatch_planner.clas.abap", + "description": "Dispatch planner", + "testclasses_file": "reference/dispatch_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "linear_search_in_loop", + "table_kind" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0115/faulty/m0_refill_planner.clas.abap b/tasks_gen/eval/G0115/faulty/m0_refill_planner.clas.abap new file mode 100644 index 0000000..a82c768 --- /dev/null +++ b/tasks_gen/eval/G0115/faulty/m0_refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_true. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count <= 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity > 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index + 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines + 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/faulty/m1_refill_planner.clas.abap b/tasks_gen/eval/G0115/faulty/m1_refill_planner.clas.abap new file mode 100644 index 0000000..53e5c82 --- /dev/null +++ b/tasks_gen/eval/G0115/faulty/m1_refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count > 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity > 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index + 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines + 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/faulty/m2_refill_planner.clas.abap b/tasks_gen/eval/G0115/faulty/m2_refill_planner.clas.abap new file mode 100644 index 0000000..eb9f079 --- /dev/null +++ b/tasks_gen/eval/G0115/faulty/m2_refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count <= 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity <= 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index + 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines + 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/faulty/m3_refill_planner.clas.abap b/tasks_gen/eval/G0115/faulty/m3_refill_planner.clas.abap new file mode 100644 index 0000000..f1e247e --- /dev/null +++ b/tasks_gen/eval/G0115/faulty/m3_refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count <= 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity > 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index - 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines + 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/faulty/m4_refill_planner.clas.abap b/tasks_gen/eval/G0115/faulty/m4_refill_planner.clas.abap new file mode 100644 index 0000000..af2a849 --- /dev/null +++ b/tasks_gen/eval/G0115/faulty/m4_refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count <= 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity > 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index + 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines - 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/generation.json b/tasks_gen/eval/G0115/generation.json new file mode 100644 index 0000000..e0f91ca --- /dev/null +++ b/tasks_gen/eval/G0115/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0115", + "pool": "eval", + "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/eval/G0115/hidden/refill_hidden.clas.abap b/tasks_gen/eval/G0115/hidden/refill_hidden.clas.abap new file mode 100644 index 0000000..2688d68 --- /dev/null +++ b/tasks_gen/eval/G0115/hidden/refill_hidden.clas.abap @@ -0,0 +1,151 @@ +CLASS {{p}}refill_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}refill_planner. + METHODS setup. + METHODS adds_and_replaces FOR TESTING. + METHODS ids_are_upper_case FOR TESTING. + METHODS rejects_zero_qty FOR TESTING. + METHODS unknown_machine FOR TESTING. + METHODS refill_accumulates FOR TESTING. + METHODS sorts_by_fill FOR TESTING. + METHODS fill_level_boundary FOR TESTING. + METHODS top_by_quantity FOR TESTING. + METHODS district_totals FOR TESTING. + METHODS total_quantity FOR TESTING. +ENDCLASS. + + +CLASS {{p}}refill_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}refill_planner( ). + ENDMETHOD. + + METHOD adds_and_replaces. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 20 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 5 ). + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D2' iv_fill_level = 30 ). + DATA(lt_machines) = mo_cut->machines_below( 100 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_machines ) ). + LOOP AT lt_machines INTO DATA(ls_machine). + cl_abap_unit_assert=>assert_equals( exp = 'D2' act = ls_machine-district ). + cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_machine-fill_level ). + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = 5 act = mo_cut->get_refill( 'M1' ) ). + ENDMETHOD. + + METHOD ids_are_upper_case. + mo_cut->add_machine( iv_machine_id = 'm1' iv_district = 'd1' iv_fill_level = 10 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 4 ) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = mo_cut->get_refill( 'm1' ) ). + ENDMETHOD. + + METHOD rejects_zero_qty. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 0 ) ). + cl_abap_unit_assert=>assert_equals( exp = abap_false + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = -3 ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->get_refill( 'M1' ) ). + ENDMETHOD. + + METHOD unknown_machine. + cl_abap_unit_assert=>assert_equals( exp = abap_false + act = mo_cut->add_refill( iv_machine_id = 'M9' iv_quantity = 5 ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->get_refill( 'M9' ) ). + ENDMETHOD. + + METHOD refill_accumulates. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + cl_abap_unit_assert=>assert_equals( exp = abap_true + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 5 ) ). + cl_abap_unit_assert=>assert_equals( exp = abap_true + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 7 ) ). + cl_abap_unit_assert=>assert_equals( exp = 12 act = mo_cut->get_refill( 'M1' ) ). + ENDMETHOD. + + METHOD sorts_by_fill. + DATA lv_position TYPE i. + mo_cut->add_machine( iv_machine_id = 'M3' iv_district = 'D1' iv_fill_level = 30 ). + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D2' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 20 ). + DATA(lt_machines) = mo_cut->machines_below( 25 ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_machines ) ). + LOOP AT lt_machines INTO DATA(ls_machine). + lv_position = lv_position + 1. + IF lv_position = 1. + cl_abap_unit_assert=>assert_equals( exp = 'M1' act = ls_machine-machine_id ). + ELSEIF lv_position = 2. + cl_abap_unit_assert=>assert_equals( exp = 'M2' act = ls_machine-machine_id ). + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD fill_level_boundary. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 20 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 19 ). + DATA(lt_below_20) = mo_cut->machines_below( 20 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_below_20 ) ). + DATA(lt_below_0) = mo_cut->machines_below( 0 ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_below_0 ) ). + ENDMETHOD. + + METHOD top_by_quantity. + DATA lv_position TYPE i. + mo_cut->add_machine( iv_machine_id = 'MA' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'MB' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'MC' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'MD' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'MA' iv_quantity = 5 ). + mo_cut->add_refill( iv_machine_id = 'MB' iv_quantity = 9 ). + mo_cut->add_refill( iv_machine_id = 'MC' iv_quantity = 9 ). + mo_cut->add_refill( iv_machine_id = 'MD' iv_quantity = 1 ). + DATA(lt_top) = mo_cut->top_refills( 2 ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_top ) ). + LOOP AT lt_top INTO DATA(ls_refill). + lv_position = lv_position + 1. + IF lv_position = 1. + cl_abap_unit_assert=>assert_equals( exp = 'MB' act = ls_refill-machine_id ). + ELSEIF lv_position = 2. + cl_abap_unit_assert=>assert_equals( exp = 'MC' act = ls_refill-machine_id ). + ENDIF. + ENDLOOP. + DATA(lt_all) = mo_cut->top_refills( 99 ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_all ) ). + DATA(lt_none) = mo_cut->top_refills( 0 ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_none ) ). + ENDMETHOD. + + METHOD district_totals. + DATA lv_position TYPE i. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D2' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M3' iv_district = 'D2' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 4 ). + mo_cut->add_refill( iv_machine_id = 'M2' iv_quantity = 6 ). + DATA(lt_summary) = mo_cut->district_summary( ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_summary ) ). + LOOP AT lt_summary INTO DATA(ls_row). + lv_position = lv_position + 1. + IF lv_position = 1. + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = ls_row-district ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-machines ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = ls_row-quantity ). + ELSEIF lv_position = 2. + cl_abap_unit_assert=>assert_equals( exp = 'D2' act = ls_row-district ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-machines ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-quantity ). + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD total_quantity. + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->total_quantity( ) ). + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D2' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 5 ). + mo_cut->add_refill( iv_machine_id = 'M2' iv_quantity = 8 ). + cl_abap_unit_assert=>assert_equals( exp = 13 act = mo_cut->total_quantity( ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/mutation.json b/tasks_gen/eval/G0115/mutation.json new file mode 100644 index 0000000..0575219 --- /dev/null +++ b/tasks_gen/eval/G0115/mutation.json @@ -0,0 +1,54 @@ +{ + "task": "G0115", + "mutants": [ + { + "object": "{{P}}REFILL_PLANNER", + "mutant": "line 79: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "REJECTS_ZERO_QTY" + ] + }, + { + "object": "{{P}}REFILL_PLANNER", + "mutant": "line 114: <= -> > (rel)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "TOP_BY_QUANTITY" + ] + }, + { + "object": "{{P}}REFILL_PLANNER", + "mutant": "line 118: > -> <= (rel)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "TOP_BY_QUANTITY" + ] + }, + { + "object": "{{P}}REFILL_PLANNER", + "mutant": "line 125: + -> - (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "TOP_BY_QUANTITY" + ] + }, + { + "object": "{{P}}REFILL_PLANNER", + "mutant": "line 148: + -> - (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "DISTRICT_TOTALS" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0115/reference/refill_planner.clas.abap b/tasks_gen/eval/G0115/reference/refill_planner.clas.abap new file mode 100644 index 0000000..0ead50d --- /dev/null +++ b/tasks_gen/eval/G0115/reference/refill_planner.clas.abap @@ -0,0 +1,164 @@ +CLASS {{p}}refill_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_machine_id TYPE c LENGTH 8, + ty_district TYPE c LENGTH 4, + ty_quantity TYPE i, + ty_fill_level TYPE i, + BEGIN OF ty_machine, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + END OF ty_machine, + tt_machines TYPE STANDARD TABLE OF ty_machine WITH EMPTY KEY, + BEGIN OF ty_refill, + machine_id TYPE ty_machine_id, + quantity TYPE ty_quantity, + END OF ty_refill, + tt_refills TYPE STANDARD TABLE OF ty_refill WITH EMPTY KEY, + BEGIN OF ty_district_summary, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_summary, + tt_district_summary TYPE STANDARD TABLE OF ty_district_summary WITH EMPTY KEY. + + METHODS add_machine + IMPORTING iv_machine_id TYPE ty_machine_id + iv_district TYPE ty_district + iv_fill_level TYPE ty_fill_level. + METHODS add_refill + IMPORTING iv_machine_id TYPE ty_machine_id + iv_quantity TYPE ty_quantity + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS get_refill + IMPORTING iv_machine_id TYPE ty_machine_id + RETURNING VALUE(rv_quantity) TYPE ty_quantity. + METHODS machines_below + IMPORTING iv_fill_level TYPE ty_fill_level + RETURNING VALUE(rt_machines) TYPE tt_machines. + METHODS top_refills + IMPORTING iv_count TYPE i + RETURNING VALUE(rt_refills) TYPE tt_refills. + METHODS district_summary + RETURNING VALUE(rt_summary) TYPE tt_district_summary. + METHODS total_quantity + RETURNING VALUE(rv_total) TYPE ty_quantity. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_machine_state, + machine_id TYPE ty_machine_id, + district TYPE ty_district, + fill_level TYPE ty_fill_level, + quantity TYPE ty_quantity, + END OF ty_machine_state, + tt_machine_state TYPE HASHED TABLE OF ty_machine_state WITH UNIQUE KEY machine_id. + + DATA mt_machines TYPE tt_machine_state. +ENDCLASS. + + +CLASS {{p}}refill_planner IMPLEMENTATION. + METHOD add_machine. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + -district = to_upper( iv_district ). + -fill_level = iv_fill_level. + ELSE. + INSERT VALUE #( machine_id = lv_machine_id + district = to_upper( iv_district ) + fill_level = iv_fill_level + quantity = 0 ) INTO TABLE mt_machines. + ENDIF. + ENDMETHOD. + + METHOD add_refill. + IF iv_quantity <= 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + rv_accepted = abap_false. + RETURN. + ENDIF. + -quantity = -quantity + iv_quantity. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD get_refill. + DATA(lv_machine_id) = to_upper( iv_machine_id ). + ASSIGN mt_machines[ machine_id = lv_machine_id ] TO FIELD-SYMBOL(). + IF sy-subrc = 0. + rv_quantity = -quantity. + ENDIF. + ENDMETHOD. + + METHOD machines_below. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-fill_level < iv_fill_level. + INSERT VALUE #( machine_id = ls_machine-machine_id + district = ls_machine-district + fill_level = ls_machine-fill_level ) INTO TABLE rt_machines. + ENDIF. + ENDLOOP. + SORT rt_machines BY fill_level machine_id. + ENDMETHOD. + + METHOD top_refills. + DATA lv_index TYPE i. + DATA lt_refills TYPE tt_refills. + IF iv_count <= 0. + RETURN. + ENDIF. + LOOP AT mt_machines INTO DATA(ls_machine). + IF ls_machine-quantity > 0. + INSERT VALUE #( machine_id = ls_machine-machine_id + quantity = ls_machine-quantity ) INTO TABLE lt_refills. + ENDIF. + ENDLOOP. + SORT lt_refills BY quantity DESCENDING machine_id ASCENDING. + LOOP AT lt_refills INTO DATA(ls_refill). + lv_index = lv_index + 1. + IF lv_index > iv_count. + EXIT. + ENDIF. + INSERT ls_refill INTO TABLE rt_refills. + ENDLOOP. + ENDMETHOD. + + METHOD district_summary. + TYPES: + BEGIN OF ty_district_state, + district TYPE ty_district, + machines TYPE i, + quantity TYPE ty_quantity, + END OF ty_district_state, + tt_district_state TYPE HASHED TABLE OF ty_district_state WITH UNIQUE KEY district. + DATA lt_districts TYPE tt_district_state. + LOOP AT mt_machines INTO DATA(ls_machine). + ASSIGN lt_districts[ district = ls_machine-district ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( district = ls_machine-district ) + INTO TABLE lt_districts ASSIGNING . + ENDIF. + -machines = -machines + 1. + -quantity = -quantity + ls_machine-quantity. + ENDLOOP. + LOOP AT lt_districts INTO DATA(ls_district). + INSERT VALUE #( district = ls_district-district + machines = ls_district-machines + quantity = ls_district-quantity ) INTO TABLE rt_summary. + ENDLOOP. + SORT rt_summary BY district. + ENDMETHOD. + + METHOD total_quantity. + LOOP AT mt_machines INTO DATA(ls_machine). + rv_total = rv_total + ls_machine-quantity. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/reference/refill_planner.testclasses.abap b/tasks_gen/eval/G0115/reference/refill_planner.testclasses.abap new file mode 100644 index 0000000..2ca74ce --- /dev/null +++ b/tasks_gen/eval/G0115/reference/refill_planner.testclasses.abap @@ -0,0 +1,93 @@ +CLASS ltc_refill_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}refill_planner. + METHODS setup. + METHODS adds_machine FOR TESTING. + METHODS replaces_machine FOR TESTING. + METHODS accumulates_refill FOR TESTING. + METHODS rejects_bad_quantity FOR TESTING. + METHODS ignores_unknown_machine FOR TESTING. + METHODS sorts_low_fill_level FOR TESTING. + METHODS ranks_largest_refills FOR TESTING. + METHODS sums_per_district FOR TESTING. +ENDCLASS. + + +CLASS ltc_refill_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}refill_planner( ). + ENDMETHOD. + + METHOD adds_machine. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + DATA(lt_machines) = mo_cut->machines_below( 50 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_machines ) ). + ENDMETHOD. + + METHOD replaces_machine. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D2' iv_fill_level = 20 ). + DATA(lt_machines) = mo_cut->machines_below( 50 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_machines ) ). + LOOP AT lt_machines INTO DATA(ls_machine). + cl_abap_unit_assert=>assert_equals( exp = 'D2' act = ls_machine-district ). + ENDLOOP. + ENDMETHOD. + + METHOD accumulates_refill. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 5 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 7 ). + cl_abap_unit_assert=>assert_equals( exp = 12 act = mo_cut->get_refill( 'M1' ) ). + ENDMETHOD. + + METHOD rejects_bad_quantity. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + cl_abap_unit_assert=>assert_equals( exp = abap_false + act = mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 0 ) ). + ENDMETHOD. + + METHOD ignores_unknown_machine. + cl_abap_unit_assert=>assert_equals( exp = abap_false + act = mo_cut->add_refill( iv_machine_id = 'M9' iv_quantity = 5 ) ). + ENDMETHOD. + + METHOD sorts_low_fill_level. + DATA lv_position TYPE i. + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 20 ). + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + DATA(lt_machines) = mo_cut->machines_below( 25 ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_machines ) ). + LOOP AT lt_machines INTO DATA(ls_machine). + lv_position = lv_position + 1. + IF lv_position = 1. + cl_abap_unit_assert=>assert_equals( exp = 'M1' act = ls_machine-machine_id ). + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD ranks_largest_refills. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 3 ). + mo_cut->add_refill( iv_machine_id = 'M2' iv_quantity = 8 ). + DATA(lt_refills) = mo_cut->top_refills( 1 ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_refills ) ). + LOOP AT lt_refills INTO DATA(ls_refill). + cl_abap_unit_assert=>assert_equals( exp = 'M2' act = ls_refill-machine_id ). + ENDLOOP. + ENDMETHOD. + + METHOD sums_per_district. + mo_cut->add_machine( iv_machine_id = 'M1' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_machine( iv_machine_id = 'M2' iv_district = 'D1' iv_fill_level = 10 ). + mo_cut->add_refill( iv_machine_id = 'M1' iv_quantity = 3 ). + mo_cut->add_refill( iv_machine_id = 'M2' iv_quantity = 8 ). + DATA(lt_summary) = mo_cut->district_summary( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_summary ) ). + LOOP AT lt_summary INTO DATA(ls_row). + cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-machines ). + cl_abap_unit_assert=>assert_equals( exp = 11 act = ls_row-quantity ). + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0115/spec.md b/tasks_gen/eval/G0115/spec.md new file mode 100644 index 0000000..f17c4c5 --- /dev/null +++ b/tasks_gen/eval/G0115/spec.md @@ -0,0 +1,98 @@ +# 1. Goal +A vending machine operator plans the refill of its vending machines. A planning +program collects the machines and the refill quantities. The program then asks +the planner for the machines with a low fill level, for the largest refills, and +for the totals per district. The program needs one class for this work. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP is the development package. +- Only the class is in scope. The planning program is not in scope. +- A machine has a machine ID, a district, and a fill level in percent. +- A refill is a quantity of items for one machine. +- The class holds the machines of one planning run in memory. +- One program calls the class many times during a planning run. + +# 4. Contract +Create the class {{P}}REFILL_PLANNER in package $TMP. +The class is public and final. It has a public constructor without parameters. +Do not add other public methods. + +The public section of the class contains these types: +- ty_machine_id TYPE c LENGTH 8 +- ty_district TYPE c LENGTH 4 +- ty_quantity TYPE i +- ty_fill_level TYPE i +- ty_machine with the components machine_id TYPE ty_machine_id, + district TYPE ty_district, and fill_level TYPE ty_fill_level +- tt_machines with the line type ty_machine +- ty_refill with the components machine_id TYPE ty_machine_id and + quantity TYPE ty_quantity +- tt_refills with the line type ty_refill +- ty_district_summary with the components district TYPE ty_district, + machines TYPE i, and quantity TYPE ty_quantity +- tt_district_summary with the line type ty_district_summary + +The class has these public methods: +- add_machine with the importing parameters iv_machine_id TYPE ty_machine_id, + iv_district TYPE ty_district, and iv_fill_level TYPE ty_fill_level. +- add_refill with the importing parameters iv_machine_id TYPE ty_machine_id and + iv_quantity TYPE ty_quantity and with the returning parameter rv_accepted TYPE + abap_bool. +- get_refill with the importing parameter iv_machine_id TYPE ty_machine_id and + the returning parameter rv_quantity TYPE ty_quantity. +- machines_below with the importing parameter iv_fill_level TYPE ty_fill_level + and the returning parameter rt_machines TYPE tt_machines. +- top_refills with the importing parameter iv_count TYPE i and the returning + parameter rt_refills TYPE tt_refills. +- district_summary with the returning parameter rt_summary TYPE + tt_district_summary. +- total_quantity with the returning parameter rv_total TYPE ty_quantity. + +# 5. Business rules +1. A new instance of the class holds no machine. +2. The key of a machine is the machine ID. Two machines with the same machine ID + are one machine. +3. Convert every machine ID and every district to upper case before the class + stores it or compares it. +4. add_machine: If the machine ID is not known, add the machine with the given + district and the given fill level. If the machine ID is known, replace the + district and the fill level of the machine. The refill quantity of the + machine does not change. +5. add_refill: If iv_quantity is zero or less, return abap_false and change + nothing. +6. add_refill: If the machine ID is not known, return abap_false and change + nothing. +7. add_refill: Else add iv_quantity to the refill quantity of the machine and + return abap_true. +8. get_refill: Return the refill quantity of the machine. Return 0 if the + machine has no refill quantity or if the machine ID is not known. +9. machines_below: Return all machines with a fill level that is less than + iv_fill_level. Sort the result by fill level in ascending order. Sort + machines with the same fill level by machine ID in ascending order. +10. top_refills: Return the machines that have a refill quantity greater than + zero. The result has at most iv_count entries. Sort the result by refill + quantity in descending order. Sort machines with the same refill quantity + by machine ID in ascending order. If iv_count is zero or less, return an + empty table. If fewer machines have a refill quantity, return all of them. +11. district_summary: Return one row for each district that has at least one + machine. machines is the number of machines in the district. quantity is + the sum of the refill quantities of the machines in the district. Sort the + result by district in ascending order. +12. total_quantity: Return the sum of the refill quantities of all machines. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Coding standards: Clean ABAP. Keep every method below 40 statements. Use no + global variable. Add no comment that restates the code. Pass large parameters + by reference. +- Out of scope: database tables, CDS views, reports, function modules, and + screen programming. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. +- The class has no ATC finding of priority 1 or priority 2. diff --git a/tasks_gen/eval/G0115/task.json b/tasks_gen/eval/G0115/task.json new file mode 100644 index 0000000..216d2f5 --- /dev/null +++ b/tasks_gen/eval/G0115/task.json @@ -0,0 +1,42 @@ +{ + "id": "G0115", + "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}}REFILL_PLANNER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}REFILL_HIDDEN", + "file": "hidden/refill_hidden.clas.abap", + "description": "Hidden tests for the refill planner" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}REFILL_PLANNER", + "file": "reference/refill_planner.clas.abap", + "description": "Refill planner for vending machines", + "testclasses_file": "reference/refill_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "linear_search_in_loop", + "nested_loops" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0116/faulty/m0_departure_planner.clas.abap b/tasks_gen/eval/G0116/faulty/m0_departure_planner.clas.abap new file mode 100644 index 0000000..5c781b6 --- /dev/null +++ b/tasks_gen/eval/G0116/faulty/m0_departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start <> 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart >= iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/faulty/m1_departure_planner.clas.abap b/tasks_gen/eval/G0116/faulty/m1_departure_planner.clas.abap new file mode 100644 index 0000000..128d7be --- /dev/null +++ b/tasks_gen/eval/G0116/faulty/m1_departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop = iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart >= iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/faulty/m2_departure_planner.clas.abap b/tasks_gen/eval/G0116/faulty/m2_departure_planner.clas.abap new file mode 100644 index 0000000..e3c71a2 --- /dev/null +++ b/tasks_gen/eval/G0116/faulty/m2_departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart < iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/faulty/m3_departure_planner.clas.abap b/tasks_gen/eval/G0116/faulty/m3_departure_planner.clas.abap new file mode 100644 index 0000000..797b7ee --- /dev/null +++ b/tasks_gen/eval/G0116/faulty/m3_departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart >= iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start <> 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/faulty/m4_departure_planner.clas.abap b/tasks_gen/eval/G0116/faulty/m4_departure_planner.clas.abap new file mode 100644 index 0000000..f0f61bd --- /dev/null +++ b/tasks_gen/eval/G0116/faulty/m4_departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart >= iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop = iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/generation.json b/tasks_gen/eval/G0116/generation.json new file mode 100644 index 0000000..8402e02 --- /dev/null +++ b/tasks_gen/eval/G0116/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0116", + "pool": "eval", + "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/eval/G0116/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0116/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..bc2196c --- /dev/null +++ b/tasks_gen/eval/G0116/hidden/t02_hidden.clas.abap @@ -0,0 +1,133 @@ +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}}departure_planner. + METHODS setup. + METHODS empty_planner FOR TESTING. + METHODS add_counts FOR TESTING. + METHODS duplicate_ignored FOR TESTING. + METHODS duplicate_in_one_call FOR TESTING. + METHODS next_earliest FOR TESTING. + METHODS next_from_exact_time FOR TESTING. + METHODS next_after_last FOR TESTING. + METHODS next_unknown_stop FOR TESTING. + METHODS stop_list_sorted FOR TESTING. + METHODS stop_list_unknown FOR TESTING. + METHODS add_empty_no_change FOR TESTING. + METHODS departures_d1 + RETURNING VALUE(rt_departures) TYPE {{p}}departure_planner=>tt_departures. + METHODS departures_d2 + RETURNING VALUE(rt_departures) TYPE {{p}}departure_planner=>tt_departures. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}departure_planner( ). + ENDMETHOD. + + METHOD departures_d1. + rt_departures = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) + ( stop = 'A100' line = 'L20' depart = '083000' ) + ( stop = 'B200' line = 'L10' depart = '090000' ) ). + ENDMETHOD. + + METHOD departures_d2. + rt_departures = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) + ( stop = 'A100' line = 'L15' depart = '081500' ) + ( stop = 'A100' line = 'L30' depart = '081500' ) + ( stop = 'C300' line = 'L30' depart = '070000' ) ). + ENDMETHOD. + + METHOD empty_planner. + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->count( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '000000' ). + cl_abap_unit_assert=>assert_initial( act = ls_next-stop ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = lines( mo_cut->departures_at_stop( 'A100' ) ) ). + ENDMETHOD. + + METHOD add_counts. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = mo_cut->count( ) ). + ENDMETHOD. + + METHOD duplicate_ignored. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + mo_cut->add_depot( iv_depot = 'D2' it_departures = departures_d2( ) ). + cl_abap_unit_assert=>assert_equals( exp = 6 act = mo_cut->count( ) ). + DATA lt_stop TYPE {{p}}departure_planner=>tt_departures. + lt_stop = mo_cut->departures_at_stop( 'A100' ). + DATA ls_row TYPE {{p}}departure_planner=>ty_departure. + READ TABLE lt_stop INTO ls_row WITH KEY line = 'L10' depart = '080000'. + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = ls_row-depot ). + ENDMETHOD. + + METHOD duplicate_in_one_call. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = departures_d1( ). + DATA ls_first TYPE {{p}}departure_planner=>ty_departure. + ls_first = lt_input[ 1 ]. + APPEND ls_first TO lt_input. + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = mo_cut->count( ) ). + ENDMETHOD. + + METHOD next_earliest. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '000000' ). + cl_abap_unit_assert=>assert_equals( exp = 'L10' act = ls_next-line ). + cl_abap_unit_assert=>assert_equals( exp = '080000' act = ls_next-depart ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = ls_next-depot ). + ENDMETHOD. + + METHOD next_from_exact_time. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '083000' ). + cl_abap_unit_assert=>assert_equals( exp = 'L20' act = ls_next-line ). + cl_abap_unit_assert=>assert_equals( exp = '083000' act = ls_next-depart ). + ENDMETHOD. + + METHOD next_after_last. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '083001' ). + cl_abap_unit_assert=>assert_initial( act = ls_next-stop ). + ENDMETHOD. + + METHOD next_unknown_stop. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'Z999' iv_from = '000000' ). + cl_abap_unit_assert=>assert_initial( act = ls_next-stop ). + ENDMETHOD. + + METHOD stop_list_sorted. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + mo_cut->add_depot( iv_depot = 'D2' it_departures = departures_d2( ) ). + DATA lt_stop TYPE {{p}}departure_planner=>tt_departures. + lt_stop = mo_cut->departures_at_stop( 'A100' ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines( lt_stop ) ). + cl_abap_unit_assert=>assert_equals( exp = '080000' act = lt_stop[ 1 ]-depart ). + cl_abap_unit_assert=>assert_equals( exp = '081500' act = lt_stop[ 2 ]-depart ). + cl_abap_unit_assert=>assert_equals( exp = 'L15' act = lt_stop[ 2 ]-line ). + cl_abap_unit_assert=>assert_equals( exp = 'L30' act = lt_stop[ 3 ]-line ). + cl_abap_unit_assert=>assert_equals( exp = '083000' act = lt_stop[ 4 ]-depart ). + ENDMETHOD. + + METHOD stop_list_unknown. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 + act = lines( mo_cut->departures_at_stop( 'Z999' ) ) ). + ENDMETHOD. + + METHOD add_empty_no_change. + mo_cut->add_depot( iv_depot = 'D1' it_departures = departures_d1( ) ). + DATA lt_empty TYPE {{p}}departure_planner=>tt_departures. + mo_cut->add_depot( iv_depot = 'D2' it_departures = lt_empty ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = mo_cut->count( ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/mutation.json b/tasks_gen/eval/G0116/mutation.json new file mode 100644 index 0000000..ea793bf --- /dev/null +++ b/tasks_gen/eval/G0116/mutation.json @@ -0,0 +1,60 @@ +{ + "task": "G0116", + "mutants": [ + { + "object": "{{P}}DEPARTURE_PLANNER", + "mutant": "line 56: = -> <> (eq)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "NEXT_EARLIEST", + "NEXT_FROM_EXACT_TIME" + ] + }, + { + "object": "{{P}}DEPARTURE_PLANNER", + "mutant": "line 60: <> -> = (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "NEXT_EARLIEST", + "NEXT_FROM_EXACT_TIME" + ] + }, + { + "object": "{{P}}DEPARTURE_PLANNER", + "mutant": "line 63: >= -> < (rel)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "NEXT_AFTER_LAST", + "NEXT_EARLIEST", + "NEXT_FROM_EXACT_TIME" + ] + }, + { + "object": "{{P}}DEPARTURE_PLANNER", + "mutant": "line 72: = -> <> (eq)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "DUPLICATE_IGNORED", + "STOP_LIST_SORTED" + ] + }, + { + "object": "{{P}}DEPARTURE_PLANNER", + "mutant": "line 76: <> -> = (rel)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "DUPLICATE_IGNORED", + "STOP_LIST_SORTED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0116/reference/departure_planner.clas.abap b/tasks_gen/eval/G0116/reference/departure_planner.clas.abap new file mode 100644 index 0000000..3a357b1 --- /dev/null +++ b/tasks_gen/eval/G0116/reference/departure_planner.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}departure_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_stop TYPE c LENGTH 4. + TYPES ty_line TYPE c LENGTH 6. + TYPES ty_depot TYPE c LENGTH 4. + TYPES ty_time TYPE t. + TYPES: + BEGIN OF ty_departure, + stop TYPE ty_stop, + line TYPE ty_line, + depart TYPE ty_time, + depot TYPE ty_depot, + END OF ty_departure, + tt_departures TYPE STANDARD TABLE OF ty_departure WITH EMPTY KEY. + + METHODS constructor. + METHODS add_depot + IMPORTING iv_depot TYPE ty_depot + it_departures TYPE tt_departures. + METHODS next_departure + IMPORTING iv_stop TYPE ty_stop + iv_from TYPE ty_time + RETURNING VALUE(rs_departure) TYPE ty_departure. + METHODS departures_at_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rt_departures) TYPE tt_departures. + METHODS count + RETURNING VALUE(rv_count) TYPE i. + + PRIVATE SECTION. + TYPES tt_store TYPE SORTED TABLE OF ty_departure + WITH UNIQUE KEY stop depart line. + DATA mt_store TYPE tt_store. + + METHODS first_index_of_stop + IMPORTING iv_stop TYPE ty_stop + RETURNING VALUE(rv_index) TYPE i. +ENDCLASS. + + +CLASS {{p}}departure_planner IMPLEMENTATION. + METHOD constructor. + CLEAR mt_store. + ENDMETHOD. + + METHOD add_depot. + LOOP AT it_departures INTO DATA(ls_departure). + DATA(ls_store) = ls_departure. + ls_store-depot = iv_depot. + INSERT ls_store INTO TABLE mt_store. + ENDLOOP. + ENDMETHOD. + + METHOD next_departure. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + IF ls_departure-depart >= iv_from. + rs_departure = ls_departure. + RETURN. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD departures_at_stop. + DATA(lv_start) = first_index_of_stop( iv_stop ). + IF lv_start = 0. + RETURN. + ENDIF. + LOOP AT mt_store INTO DATA(ls_departure) FROM lv_start. + IF ls_departure-stop <> iv_stop. + EXIT. + ENDIF. + APPEND ls_departure TO rt_departures. + ENDLOOP. + ENDMETHOD. + + METHOD count. + rv_count = lines( mt_store ). + ENDMETHOD. + + METHOD first_index_of_stop. + READ TABLE mt_store TRANSPORTING NO FIELDS WITH KEY stop = iv_stop. + IF sy-subrc = 0. + rv_index = sy-tabix. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/reference/departure_planner.testclasses.abap b/tasks_gen/eval/G0116/reference/departure_planner.testclasses.abap new file mode 100644 index 0000000..d4d17ec --- /dev/null +++ b/tasks_gen/eval/G0116/reference/departure_planner.testclasses.abap @@ -0,0 +1,73 @@ +CLASS ltc_departure_planner DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}departure_planner. + METHODS setup. + METHODS stores_depot FOR TESTING. + METHODS ignores_duplicate FOR TESTING. + METHODS next_departure_ok FOR TESTING. + METHODS next_departure_none FOR TESTING. + METHODS stop_list_ok FOR TESTING. + METHODS empty_input_ok FOR TESTING. +ENDCLASS. + + +CLASS ltc_departure_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}departure_planner( ). + ENDMETHOD. + + METHOD stores_depot. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) ). + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = mo_cut->count( ) ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '000000' ). + cl_abap_unit_assert=>assert_equals( exp = 'D1' act = ls_next-depot ). + ENDMETHOD. + + METHOD ignores_duplicate. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) ). + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + mo_cut->add_depot( iv_depot = 'D2' it_departures = lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = mo_cut->count( ) ). + ENDMETHOD. + + METHOD next_departure_ok. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) + ( stop = 'A100' line = 'L20' depart = '083000' ) ). + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '080001' ). + cl_abap_unit_assert=>assert_equals( exp = 'L20' act = ls_next-line ). + ENDMETHOD. + + METHOD next_departure_none. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = VALUE #( ( stop = 'A100' line = 'L10' depart = '080000' ) ). + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + DATA ls_next TYPE {{p}}departure_planner=>ty_departure. + ls_next = mo_cut->next_departure( iv_stop = 'A100' iv_from = '090000' ). + cl_abap_unit_assert=>assert_initial( act = ls_next-stop ). + ENDMETHOD. + + METHOD stop_list_ok. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + lt_input = VALUE #( ( stop = 'A100' line = 'L20' depart = '083000' ) + ( stop = 'A100' line = 'L10' depart = '080000' ) ). + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + DATA lt_stop TYPE {{p}}departure_planner=>tt_departures. + lt_stop = mo_cut->departures_at_stop( 'A100' ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_stop ) ). + cl_abap_unit_assert=>assert_equals( exp = '080000' act = lt_stop[ 1 ]-depart ). + ENDMETHOD. + + METHOD empty_input_ok. + DATA lt_input TYPE {{p}}departure_planner=>tt_departures. + mo_cut->add_depot( iv_depot = 'D1' it_departures = lt_input ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->count( ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0116/spec.md b/tasks_gen/eval/G0116/spec.md new file mode 100644 index 0000000..0e15198 --- /dev/null +++ b/tasks_gen/eval/G0116/spec.md @@ -0,0 +1,68 @@ +# 1. Goal +A regional bus company collects the departure timetables of its depots. The +planning office needs one class that stores the departures of all depots and +answers two questions: which departure comes next at a stop, and which +departures a stop has. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP, client 001, SAP_BASIS 816. +- There are no seed objects. The class is new. +- A departure has a stop, a line, a departure time and the depot that reported + it. + +# 4. Contract +Create the class {{P}}DEPARTURE_PLANNER in package $TMP. +The class is public and final. It has a public constructor without parameters. + +The class declares these public types: +- TY_STOP: character, length 4. +- TY_LINE: character, length 6. +- TY_DEPOT: character, length 4. +- TY_TIME: a time of the system (type T, format HHMMSS). +- TY_DEPARTURE: a structure with the components STOP (TY_STOP), LINE (TY_LINE), + DEPART (TY_TIME) and DEPOT (TY_DEPOT), in this order. +- TT_DEPARTURES: a standard table of TY_DEPARTURE with an empty key. + +The class has exactly these public methods: +- CONSTRUCTOR: no parameters. +- ADD_DEPOT: importing IV_DEPOT type TY_DEPOT and IT_DEPARTURES type + TT_DEPARTURES. No returning parameter. +- NEXT_DEPARTURE: importing IV_STOP type TY_STOP and IV_FROM type TY_TIME. + Returning RS_DEPARTURE type TY_DEPARTURE. +- DEPARTURES_AT_STOP: importing IV_STOP type TY_STOP. Returning RT_DEPARTURES + type TT_DEPARTURES. +- COUNT: no importing parameter. Returning RV_COUNT type I. + +Do not add other public methods. + +# 5. Business rules +1. ADD_DEPOT stores the departures of the given depot. The DEPOT component of + the input rows is not used. The class stores IV_DEPOT in the DEPOT component. +2. Two departures are equal if STOP, LINE and DEPART are equal. If an equal + departure is already stored, the new departure is ignored and the stored + departure does not change. This rule also applies to equal departures inside + one call of ADD_DEPOT. +3. COUNT returns the number of stored departures. +4. NEXT_DEPARTURE returns the stored departure of the given stop with the + smallest DEPART that is not before IV_FROM. "Not before" means + DEPART >= IV_FROM. If the stop has no such departure, the result is initial. +5. DEPARTURES_AT_STOP returns all stored departures of the given stop in + ascending order of DEPART. If two departures have the same DEPART, the + departure with the smaller LINE comes first. If the stop is not stored, the + result is empty. +6. An empty input table changes nothing. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: no database access, no user interface, no change to 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/eval/G0116/task.json b/tasks_gen/eval/G0116/task.json new file mode 100644 index 0000000..e6992ca --- /dev/null +++ b/tasks_gen/eval/G0116/task.json @@ -0,0 +1,42 @@ +{ + "id": "G0116", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}DEPARTURE_PLANNER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}DEPARTURE_PLANNER", + "file": "reference/departure_planner.clas.abap", + "description": "Departure planner with a keyed internal store", + "testclasses_file": "reference/departure_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0117/faulty/m0_tool_crib.clas.abap b/tasks_gen/eval/G0117/faulty/m0_tool_crib.clas.abap new file mode 100644 index 0000000..3090061 --- /dev/null +++ b/tasks_gen/eval/G0117/faulty/m0_tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL AND lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_true. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date < iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/faulty/m1_tool_crib.clas.abap b/tasks_gen/eval/G0117/faulty/m1_tool_crib.clas.abap new file mode 100644 index 0000000..3c8bbda --- /dev/null +++ b/tasks_gen/eval/G0117/faulty/m1_tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL OR lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) <> abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_true. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date < iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/faulty/m2_tool_crib.clas.abap b/tasks_gen/eval/G0117/faulty/m2_tool_crib.clas.abap new file mode 100644 index 0000000..78f8860 --- /dev/null +++ b/tasks_gen/eval/G0117/faulty/m2_tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL OR lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date < iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/faulty/m3_tool_crib.clas.abap b/tasks_gen/eval/G0117/faulty/m3_tool_crib.clas.abap new file mode 100644 index 0000000..ceb7a75 --- /dev/null +++ b/tasks_gen/eval/G0117/faulty/m3_tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL OR lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_true. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_false. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date < iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/faulty/m4_tool_crib.clas.abap b/tasks_gen/eval/G0117/faulty/m4_tool_crib.clas.abap new file mode 100644 index 0000000..123c5c7 --- /dev/null +++ b/tasks_gen/eval/G0117/faulty/m4_tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL OR lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_true. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date >= iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/generation.json b/tasks_gen/eval/G0117/generation.json new file mode 100644 index 0000000..2ac182f --- /dev/null +++ b/tasks_gen/eval/G0117/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0117", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 95.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/eval/G0117/hidden/t11_hidden.clas.abap b/tasks_gen/eval/G0117/hidden/t11_hidden.clas.abap new file mode 100644 index 0000000..1050843 --- /dev/null +++ b/tasks_gen/eval/G0117/hidden/t11_hidden.clas.abap @@ -0,0 +1,157 @@ +CLASS {{p}}t11_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_tool_ids TYPE STANDARD TABLE OF {{p}}tool_crib=>ty_tool_id WITH EMPTY KEY. + + CONSTANTS c_drill TYPE {{p}}tool_crib=>ty_tool_id VALUE 'DRILL'. + CONSTANTS c_saw TYPE {{p}}tool_crib=>ty_tool_id VALUE 'SAW'. + CONSTANTS c_grinder TYPE {{p}}tool_crib=>ty_tool_id VALUE 'GRINDER'. + CONSTANTS c_wrench TYPE {{p}}tool_crib=>ty_tool_id VALUE 'WRENCH'. + CONSTANTS c_smith TYPE {{p}}tool_crib=>ty_employee VALUE 'SMITH'. + + DATA mo_cut TYPE REF TO {{p}}tool_crib. + + METHODS setup. + METHODS tool_ids + IMPORTING it_loans TYPE {{p}}tool_crib=>tt_loans + RETURNING VALUE(rt_ids) TYPE tt_tool_ids. + METHODS first_loan + IMPORTING it_loans TYPE {{p}}tool_crib=>tt_loans + RETURNING VALUE(rs_loan) TYPE {{p}}tool_crib=>ty_loan. + METHODS lend_new_tool_ok FOR TESTING. + METHODS lend_twice_rejected FOR TESTING. + METHODS return_tool_ok FOR TESTING. + METHODS open_loans_due_date_order FOR TESTING. + METHODS open_loans_tool_id_order FOR TESTING. + METHODS overdue_only_before_today FOR TESTING. + METHODS overdue_initial_today_empty FOR TESTING. + METHODS empty_tool_id_rejected FOR TESTING. + METHODS invalid_loan_rejected FOR TESTING. + METHODS unknown_tool_rejected FOR TESTING. + METHODS lower_case_and_spaces FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t11_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tool_crib( ). + ENDMETHOD. + + METHOD tool_ids. + LOOP AT it_loans INTO DATA(ls_loan). + INSERT ls_loan-tool_id INTO TABLE rt_ids. + ENDLOOP. + ENDMETHOD. + + METHOD first_loan. + LOOP AT it_loans INTO rs_loan. + EXIT. + ENDLOOP. + ENDMETHOD. + + METHOD lend_new_tool_ok. + cl_abap_unit_assert=>assert_true( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = c_smith + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_true( act = mo_cut->is_on_loan( c_drill ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( c_saw ) ). + ENDMETHOD. + + METHOD lend_twice_rejected. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = 'JONES' + iv_due_date = '20250210' ) ). + DATA(lt_loans) = mo_cut->open_loans( ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_loans ) ). + DATA(ls_loan) = first_loan( lt_loans ). + cl_abap_unit_assert=>assert_equals( exp = c_smith act = ls_loan-employee ). + ENDMETHOD. + + METHOD return_tool_ok. + cl_abap_unit_assert=>assert_false( act = mo_cut->return_tool( c_saw ) ). + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + cl_abap_unit_assert=>assert_true( act = mo_cut->return_tool( c_drill ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( c_drill ) ). + DATA(lt_loans) = mo_cut->open_loans( ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_loans ) ). + ENDMETHOD. + + METHOD open_loans_due_date_order. + mo_cut->lend( iv_tool_id = c_saw iv_employee = c_smith iv_due_date = '20250310' ). + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + mo_cut->lend( iv_tool_id = c_grinder iv_employee = c_smith iv_due_date = '20250210' ). + DATA(lt_loans) = mo_cut->open_loans( ). + DATA(lt_ids) = tool_ids( lt_loans ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_ids ) ). + cl_abap_unit_assert=>assert_equals( exp = c_drill act = lt_ids[ 1 ] ). + cl_abap_unit_assert=>assert_equals( exp = c_grinder act = lt_ids[ 2 ] ). + cl_abap_unit_assert=>assert_equals( exp = c_saw act = lt_ids[ 3 ] ). + ENDMETHOD. + + METHOD open_loans_tool_id_order. + mo_cut->lend( iv_tool_id = c_wrench iv_employee = c_smith iv_due_date = '20250110' ). + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + DATA(lt_loans) = mo_cut->open_loans( ). + DATA(lt_ids) = tool_ids( lt_loans ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_ids ) ). + cl_abap_unit_assert=>assert_equals( exp = c_drill act = lt_ids[ 1 ] ). + cl_abap_unit_assert=>assert_equals( exp = c_wrench act = lt_ids[ 2 ] ). + ENDMETHOD. + + METHOD overdue_only_before_today. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250109' ). + mo_cut->lend( iv_tool_id = c_saw iv_employee = c_smith iv_due_date = '20250110' ). + mo_cut->lend( iv_tool_id = c_grinder iv_employee = c_smith iv_due_date = '20250111' ). + DATA(lt_loans) = mo_cut->overdue_loans( '20250110' ). + DATA(lt_ids) = tool_ids( lt_loans ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_ids ) ). + cl_abap_unit_assert=>assert_equals( exp = c_drill act = lt_ids[ 1 ] ). + ENDMETHOD. + + METHOD overdue_initial_today_empty. + DATA lv_today TYPE d. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250109' ). + DATA(lt_loans) = mo_cut->overdue_loans( lv_today ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_loans ) ). + ENDMETHOD. + + METHOD empty_tool_id_rejected. + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = ' ' + iv_employee = c_smith + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( ' ' ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->return_tool( ' ' ) ). + DATA(lt_loans) = mo_cut->open_loans( ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_loans ) ). + ENDMETHOD. + + METHOD invalid_loan_rejected. + DATA lv_initial_date TYPE d. + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = ' ' + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = c_smith + iv_due_date = lv_initial_date ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( c_drill ) ). + ENDMETHOD. + + METHOD unknown_tool_rejected. + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = 'HAMMER' + iv_employee = c_smith + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( 'HAMMER' ) ). + ENDMETHOD. + + METHOD lower_case_and_spaces. + cl_abap_unit_assert=>assert_true( act = mo_cut->lend( iv_tool_id = 'd r i l l' + iv_employee = 'smith' + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_true( act = mo_cut->is_on_loan( c_drill ) ). + DATA(lt_loans) = mo_cut->open_loans( ). + DATA(ls_loan) = first_loan( lt_loans ). + cl_abap_unit_assert=>assert_equals( exp = c_drill act = ls_loan-tool_id ). + cl_abap_unit_assert=>assert_equals( exp = c_smith act = ls_loan-employee ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/mutation.json b/tasks_gen/eval/G0117/mutation.json new file mode 100644 index 0000000..0c5ba5c --- /dev/null +++ b/tasks_gen/eval/G0117/mutation.json @@ -0,0 +1,68 @@ +{ + "task": "G0117", + "mutants": [ + { + "object": "{{P}}TOOL_CRIB", + "mutant": "line 49: OR -> AND (logic)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "INVALID_LOAN_REJECTED" + ] + }, + { + "object": "{{P}}TOOL_CRIB", + "mutant": "line 52: = -> <> (eq)", + "status": "killed", + "hidden": "3/11", + "failed_tests": [ + "LEND_NEW_TOOL_OK", + "LEND_TWICE_REJECTED", + "LOWER_CASE_AND_SPACES", + "OPEN_LOANS_DUE_DATE_ORDER", + "OPEN_LOANS_TOOL_ID_ORDER", + "OVERDUE_ONLY_BEFORE_TODAY", + "RETURN_TOOL_OK", + "UNKNOWN_TOOL_REJECTED" + ] + }, + { + "object": "{{P}}TOOL_CRIB", + "mutant": "line 55: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "4/11", + "failed_tests": [ + "LEND_NEW_TOOL_OK", + "LEND_TWICE_REJECTED", + "LOWER_CASE_AND_SPACES", + "OPEN_LOANS_DUE_DATE_ORDER", + "OPEN_LOANS_TOOL_ID_ORDER", + "OVERDUE_ONLY_BEFORE_TODAY", + "RETURN_TOOL_OK" + ] + }, + { + "object": "{{P}}TOOL_CRIB", + "mutant": "line 63: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "9/11", + "failed_tests": [ + "LEND_NEW_TOOL_OK", + "LOWER_CASE_AND_SPACES" + ] + }, + { + "object": "{{P}}TOOL_CRIB", + "mutant": "line 94: < -> >= (rel)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "OVERDUE_ONLY_BEFORE_TODAY" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0117/reference/tool_crib.clas.abap b/tasks_gen/eval/G0117/reference/tool_crib.clas.abap new file mode 100644 index 0000000..d8f0f6d --- /dev/null +++ b/tasks_gen/eval/G0117/reference/tool_crib.clas.abap @@ -0,0 +1,107 @@ +CLASS {{p}}tool_crib DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + TYPES ty_employee TYPE c LENGTH 20. + TYPES: + BEGIN OF ty_loan, + tool_id TYPE ty_tool_id, + employee TYPE ty_employee, + due_date TYPE d, + END OF ty_loan. + TYPES tt_loans TYPE SORTED TABLE OF ty_loan + WITH NON-UNIQUE KEY due_date tool_id. + + METHODS lend + IMPORTING iv_tool_id TYPE ty_tool_id + iv_employee TYPE ty_employee + iv_due_date TYPE d + RETURNING VALUE(rv_accepted) TYPE abap_bool. + METHODS return_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_found) TYPE abap_bool. + METHODS is_on_loan + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_on_loan) TYPE abap_bool. + METHODS open_loans + RETURNING VALUE(rt_loans) TYPE tt_loans. + METHODS overdue_loans + IMPORTING iv_today TYPE d + RETURNING VALUE(rt_loans) TYPE tt_loans. + + PRIVATE SECTION. + TYPES tt_loan_store TYPE SORTED TABLE OF ty_loan WITH UNIQUE KEY tool_id. + DATA mt_loans TYPE tt_loan_store. + + METHODS normalize_tool_id + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_tool_id) TYPE ty_tool_id. + METHODS normalize_employee + IMPORTING iv_employee TYPE ty_employee + RETURNING VALUE(rv_employee) TYPE ty_employee. +ENDCLASS. + + +CLASS {{p}}tool_crib IMPLEMENTATION. + METHOD lend. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + DATA(lv_employee) = normalize_employee( iv_employee ). + + IF lv_tool_id IS INITIAL OR lv_employee IS INITIAL OR iv_due_date IS INITIAL. + RETURN. + ENDIF. + IF {{p}}tool_master=>is_valid_tool( lv_tool_id ) = abap_false. + RETURN. + ENDIF. + IF is_on_loan( lv_tool_id ) = abap_true. + RETURN. + ENDIF. + + DATA(ls_loan) = VALUE ty_loan( tool_id = lv_tool_id + employee = lv_employee + due_date = iv_due_date ). + INSERT ls_loan INTO TABLE mt_loans. + rv_accepted = abap_true. + ENDMETHOD. + + METHOD return_tool. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + DELETE TABLE mt_loans WITH TABLE KEY tool_id = lv_tool_id. + rv_found = xsdbool( sy-subrc = 0 ). + ENDMETHOD. + + METHOD is_on_loan. + DATA(lv_tool_id) = normalize_tool_id( iv_tool_id ). + IF lv_tool_id IS INITIAL. + RETURN. + ENDIF. + rv_on_loan = xsdbool( line_exists( mt_loans[ tool_id = lv_tool_id ] ) ). + ENDMETHOD. + + METHOD open_loans. + LOOP AT mt_loans INTO DATA(ls_loan). + INSERT ls_loan INTO TABLE rt_loans. + ENDLOOP. + ENDMETHOD. + + METHOD overdue_loans. + IF iv_today IS INITIAL. + RETURN. + ENDIF. + LOOP AT mt_loans INTO DATA(ls_loan). + IF ls_loan-due_date < iv_today. + INSERT ls_loan INTO TABLE rt_loans. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD normalize_tool_id. + rv_tool_id = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. + + METHOD normalize_employee. + rv_employee = to_upper( replace( val = iv_employee sub = ` ` with = `` occ = 0 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/reference/tool_crib.testclasses.abap b/tasks_gen/eval/G0117/reference/tool_crib.testclasses.abap new file mode 100644 index 0000000..2d1379d --- /dev/null +++ b/tasks_gen/eval/G0117/reference/tool_crib.testclasses.abap @@ -0,0 +1,90 @@ +CLASS ltc_tool_crib DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_tool_ids TYPE STANDARD TABLE OF {{p}}tool_crib=>ty_tool_id WITH EMPTY KEY. + + CONSTANTS c_drill TYPE {{p}}tool_crib=>ty_tool_id VALUE 'DRILL'. + CONSTANTS c_saw TYPE {{p}}tool_crib=>ty_tool_id VALUE 'SAW'. + CONSTANTS c_smith TYPE {{p}}tool_crib=>ty_employee VALUE 'SMITH'. + + DATA mo_cut TYPE REF TO {{p}}tool_crib. + + METHODS setup. + METHODS lend_records_loan FOR TESTING. + METHODS lend_twice_rejected FOR TESTING. + METHODS lend_unknown_tool FOR TESTING. + METHODS return_removes_loan FOR TESTING. + METHODS open_loans_are_sorted FOR TESTING. + METHODS overdue_loans_are_filtered FOR TESTING. + METHODS empty_input_rejected FOR TESTING. + METHODS input_is_normalized FOR TESTING. +ENDCLASS. + + +CLASS ltc_tool_crib IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}tool_crib( ). + ENDMETHOD. + + METHOD lend_records_loan. + cl_abap_unit_assert=>assert_true( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = c_smith + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_true( act = mo_cut->is_on_loan( c_drill ) ). + ENDMETHOD. + + METHOD lend_twice_rejected. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = c_smith + iv_due_date = '20250111' ) ). + ENDMETHOD. + + METHOD lend_unknown_tool. + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = 'HAMMER' + iv_employee = c_smith + iv_due_date = '20250110' ) ). + ENDMETHOD. + + METHOD return_removes_loan. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + cl_abap_unit_assert=>assert_true( act = mo_cut->return_tool( c_drill ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->is_on_loan( c_drill ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->return_tool( c_drill ) ). + ENDMETHOD. + + METHOD open_loans_are_sorted. + mo_cut->lend( iv_tool_id = c_saw iv_employee = c_smith iv_due_date = '20250310' ). + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250110' ). + DATA(lt_loans) = mo_cut->open_loans( ). + DATA(lt_ids) = VALUE tt_tool_ids( ). + LOOP AT lt_loans INTO DATA(ls_loan). + INSERT ls_loan-tool_id INTO TABLE lt_ids. + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = c_drill act = lt_ids[ 1 ] ). + cl_abap_unit_assert=>assert_equals( exp = c_saw act = lt_ids[ 2 ] ). + ENDMETHOD. + + METHOD overdue_loans_are_filtered. + mo_cut->lend( iv_tool_id = c_drill iv_employee = c_smith iv_due_date = '20250109' ). + mo_cut->lend( iv_tool_id = c_saw iv_employee = c_smith iv_due_date = '20250110' ). + DATA(lt_loans) = mo_cut->overdue_loans( '20250110' ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_loans ) ). + ENDMETHOD. + + METHOD empty_input_rejected. + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = ' ' + iv_employee = c_smith + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_false( act = mo_cut->lend( iv_tool_id = c_drill + iv_employee = ' ' + iv_due_date = '20250110' ) ). + ENDMETHOD. + + METHOD input_is_normalized. + cl_abap_unit_assert=>assert_true( act = mo_cut->lend( iv_tool_id = 'drill' + iv_employee = 'smith' + iv_due_date = '20250110' ) ). + cl_abap_unit_assert=>assert_true( act = mo_cut->is_on_loan( c_drill ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/seed/tool_master.clas.abap b/tasks_gen/eval/G0117/seed/tool_master.clas.abap new file mode 100644 index 0000000..c36fbe0 --- /dev/null +++ b/tasks_gen/eval/G0117/seed/tool_master.clas.abap @@ -0,0 +1,20 @@ +CLASS {{p}}tool_master DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_tool_id TYPE c LENGTH 10. + CLASS-METHODS is_valid_tool + IMPORTING iv_tool_id TYPE ty_tool_id + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}tool_master IMPLEMENTATION. + METHOD is_valid_tool. + DATA(lv_tool_id) = to_upper( replace( val = iv_tool_id sub = ` ` with = `` occ = 0 ) ). + CASE lv_tool_id. + WHEN 'DRILL' OR 'SAW' OR 'GRINDER' OR 'WRENCH' OR 'SANDER' OR 'RIVETER'. + rv_valid = abap_true. + WHEN OTHERS. + rv_valid = abap_false. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0117/spec.md b/tasks_gen/eval/G0117/spec.md new file mode 100644 index 0000000..5d3286e --- /dev/null +++ b/tasks_gen/eval/G0117/spec.md @@ -0,0 +1,93 @@ +# 1. Goal + +A tool crib lends workshop tools to employees. A shop-floor terminal calls one +class to record the loans and the returns and to show the open loans. The class +keeps the loans in memory. + +# 2. Open questions + +None. + +# 3. Context + +- All objects are in package $TMP. No transport is necessary. +- The class {{P}}TOOL_MASTER exists in package $TMP. It is active. It has the + public type TY_TOOL_ID (character-like, length 10) and the static method + IS_VALID_TOOL. IS_VALID_TOOL has the importing parameter IV_TOOL_ID + (TY_TOOL_ID) and the returning parameter RV_VALID (abap_bool). The method + ignores spaces and letter case. It returns abap_true only for the tools that + the crib has in stock. +- The terminal creates one instance of the new class for each shift. There is + no database table. All loans live in the instance. + +# 4. Contract + +Create the class {{P}}TOOL_CRIB in package $TMP. + +The class is public and final. An instance is created without parameters. + +Public types: +- TY_TOOL_ID: character-like, length 10. +- TY_EMPLOYEE: character-like, length 20. +- TY_LOAN: structure with the components TOOL_ID (TY_TOOL_ID), EMPLOYEE + (TY_EMPLOYEE) and DUE_DATE (type D). +- TT_LOANS: table type with the row type TY_LOAN. + +Public methods: +- LEND, importing IV_TOOL_ID (TY_TOOL_ID), IV_EMPLOYEE (TY_EMPLOYEE) and + IV_DUE_DATE (D), returning RV_ACCEPTED (abap_bool). +- RETURN_TOOL, importing IV_TOOL_ID (TY_TOOL_ID), returning RV_FOUND + (abap_bool). +- IS_ON_LOAN, importing IV_TOOL_ID (TY_TOOL_ID), returning RV_ON_LOAN + (abap_bool). +- OPEN_LOANS, returning RT_LOANS (TT_LOANS). +- OVERDUE_LOANS, importing IV_TODAY (D), returning RT_LOANS (TT_LOANS). + +Do not add other public components. + +# 5. Business rules + +Normalization: +1. The normalized tool ID is the tool ID without spaces and in upper case. +2. The normalized employee is the employee without spaces and in upper case. + +LEND applies these steps in this sequence. It stops at the first step that +rejects the loan. +3. If the normalized tool ID is empty, reject. +4. If the normalized employee is empty, reject. +5. If the due date is initial, reject. +6. If {{P}}TOOL_MASTER=>IS_VALID_TOOL returns abap_false for the normalized tool + ID, reject. +7. If the normalized tool ID is already on loan, reject. +8. Else record the loan with the normalized tool ID, the normalized employee + and the due date. Accept the loan. + +Reject means: return abap_false and do not change the loans. + +9. RETURN_TOOL: if the normalized tool ID is on loan, delete the loan and + return abap_true. Else return abap_false. An empty normalized tool ID + returns abap_false. +10. IS_ON_LOAN: return abap_true if the normalized tool ID is on loan. Else + return abap_false. An empty normalized tool ID returns abap_false. +11. OPEN_LOANS returns one row for each open loan. The rows are ordered by + DUE_DATE in ascending order. Rows with the same DUE_DATE are ordered by + TOOL_ID in ascending order. +12. OVERDUE_LOANS returns the open loans with a DUE_DATE before IV_TODAY. If + IV_TODAY is initial, the result is empty. The rows have the same order as + in OPEN_LOANS. +13. A due date in the past is permitted at LEND. Such a loan appears in + OVERDUE_LOANS. +14. A new instance has no loans. + +# 6. Constraints + +- Release target: SAP_BASIS 8.16. +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}TOOL_MASTER. + +# 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/eval/G0117/task.json b/tasks_gen/eval/G0117/task.json new file mode 100644 index 0000000..6712a4a --- /dev/null +++ b/tasks_gen/eval/G0117/task.json @@ -0,0 +1,51 @@ +{ + "id": "G0117", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 60, + "max_activations": 15 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}TOOL_MASTER", + "file": "seed/tool_master.clas.abap", + "description": "Tool master with the tool IDs that the crib has in stock" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}TOOL_CRIB" + } + ], + "out_of_scope": [ + "{{P}}TOOL_MASTER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T11_HIDDEN", + "file": "hidden/t11_hidden.clas.abap", + "description": "Hidden tests for the tool crib loan book" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}TOOL_CRIB", + "file": "reference/tool_crib.clas.abap", + "description": "Tool crib loan book", + "testclasses_file": "reference/tool_crib.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "internal_table_key", + "table_access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0118/faulty/m1_canteen_planner.clas.abap b/tasks_gen/eval/G0118/faulty/m1_canteen_planner.clas.abap new file mode 100644 index 0000000..805ca86 --- /dev/null +++ b/tasks_gen/eval/G0118/faulty/m1_canteen_planner.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}canteen_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_canteen_plan. + + PRIVATE SECTION. + TYPES tt_demand TYPE SORTED TABLE OF {{p}}if_canteen_plan=>ty_order + WITH UNIQUE KEY ingredient. + TYPES tt_master TYPE HASHED TABLE OF {{p}}if_canteen_plan=>ty_master + WITH UNIQUE KEY ingredient. + + METHODS collect_orders + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + CHANGING ct_demand TYPE tt_demand. + + METHODS collect_demand + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rt_demand) TYPE tt_demand. + + METHODS to_master + IMPORTING it_catalogue TYPE {{p}}if_canteen_plan=>tt_master + RETURNING VALUE(rt_master) TYPE tt_master. + + METHODS packs_needed + IMPORTING iv_quantity TYPE {{p}}if_canteen_plan=>ty_quantity + iv_pack_size TYPE {{p}}if_canteen_plan=>ty_quantity + RETURNING VALUE(rv_packs) TYPE i. +ENDCLASS. + + +CLASS {{p}}canteen_planner IMPLEMENTATION. + METHOD {{p}}if_canteen_plan~merge_orders. + DATA(lt_demand) = collect_demand( it_orders_a ). + collect_orders( EXPORTING it_orders = it_orders_b + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~pack_requirement. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + DATA ls_master TYPE {{p}}if_canteen_plan=>ty_master. + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + CONTINUE. + ENDIF. + ls_master = lt_master[ ingredient = ls_demand-ingredient ]. + INSERT VALUE #( ingredient = ls_demand-ingredient + packs = packs_needed( iv_quantity = ls_demand-quantity + iv_pack_size = ls_master-pack_size ) ) + INTO TABLE rt_packs. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~missing_ingredients. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + APPEND ls_demand-ingredient TO rt_missing. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF ls_order-quantity > 0. + CONTINUE. + ENDIF. + READ TABLE ct_demand ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY ingredient = ls_order-ingredient. + IF sy-subrc = 0. + -quantity = -quantity + ls_order-quantity. + ELSE. + INSERT VALUE #( ingredient = ls_order-ingredient + quantity = ls_order-quantity ) INTO TABLE ct_demand. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_demand. + DATA(lt_demand) = VALUE tt_demand( ). + collect_orders( EXPORTING it_orders = it_orders + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD to_master. + LOOP AT it_catalogue INTO DATA(ls_catalogue). + INSERT VALUE #( ingredient = ls_catalogue-ingredient + pack_size = ls_catalogue-pack_size ) INTO TABLE rt_master. + ENDLOOP. + ENDMETHOD. + + METHOD packs_needed. + rv_packs = iv_quantity DIV iv_pack_size. + IF iv_quantity MOD iv_pack_size <> 0. + rv_packs = rv_packs + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/faulty/m2_canteen_planner.clas.abap b/tasks_gen/eval/G0118/faulty/m2_canteen_planner.clas.abap new file mode 100644 index 0000000..f3eb2fc --- /dev/null +++ b/tasks_gen/eval/G0118/faulty/m2_canteen_planner.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}canteen_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_canteen_plan. + + PRIVATE SECTION. + TYPES tt_demand TYPE SORTED TABLE OF {{p}}if_canteen_plan=>ty_order + WITH UNIQUE KEY ingredient. + TYPES tt_master TYPE HASHED TABLE OF {{p}}if_canteen_plan=>ty_master + WITH UNIQUE KEY ingredient. + + METHODS collect_orders + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + CHANGING ct_demand TYPE tt_demand. + + METHODS collect_demand + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rt_demand) TYPE tt_demand. + + METHODS to_master + IMPORTING it_catalogue TYPE {{p}}if_canteen_plan=>tt_master + RETURNING VALUE(rt_master) TYPE tt_master. + + METHODS packs_needed + IMPORTING iv_quantity TYPE {{p}}if_canteen_plan=>ty_quantity + iv_pack_size TYPE {{p}}if_canteen_plan=>ty_quantity + RETURNING VALUE(rv_packs) TYPE i. +ENDCLASS. + + +CLASS {{p}}canteen_planner IMPLEMENTATION. + METHOD {{p}}if_canteen_plan~merge_orders. + DATA(lt_demand) = collect_demand( it_orders_a ). + collect_orders( EXPORTING it_orders = it_orders_b + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~pack_requirement. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + DATA ls_master TYPE {{p}}if_canteen_plan=>ty_master. + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + CONTINUE. + ENDIF. + ls_master = lt_master[ ingredient = ls_demand-ingredient ]. + INSERT VALUE #( ingredient = ls_demand-ingredient + packs = packs_needed( iv_quantity = ls_demand-quantity + iv_pack_size = ls_master-pack_size ) ) + INTO TABLE rt_packs. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~missing_ingredients. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + APPEND ls_demand-ingredient TO rt_missing. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF ls_order-quantity <= 0. + CONTINUE. + ENDIF. + READ TABLE ct_demand ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY ingredient = ls_order-ingredient. + IF sy-subrc = 0. + -quantity = -quantity - ls_order-quantity. + ELSE. + INSERT VALUE #( ingredient = ls_order-ingredient + quantity = ls_order-quantity ) INTO TABLE ct_demand. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_demand. + DATA(lt_demand) = VALUE tt_demand( ). + collect_orders( EXPORTING it_orders = it_orders + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD to_master. + LOOP AT it_catalogue INTO DATA(ls_catalogue). + INSERT VALUE #( ingredient = ls_catalogue-ingredient + pack_size = ls_catalogue-pack_size ) INTO TABLE rt_master. + ENDLOOP. + ENDMETHOD. + + METHOD packs_needed. + rv_packs = iv_quantity DIV iv_pack_size. + IF iv_quantity MOD iv_pack_size <> 0. + rv_packs = rv_packs + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/faulty/m3_canteen_planner.clas.abap b/tasks_gen/eval/G0118/faulty/m3_canteen_planner.clas.abap new file mode 100644 index 0000000..1043f8b --- /dev/null +++ b/tasks_gen/eval/G0118/faulty/m3_canteen_planner.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}canteen_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_canteen_plan. + + PRIVATE SECTION. + TYPES tt_demand TYPE SORTED TABLE OF {{p}}if_canteen_plan=>ty_order + WITH UNIQUE KEY ingredient. + TYPES tt_master TYPE HASHED TABLE OF {{p}}if_canteen_plan=>ty_master + WITH UNIQUE KEY ingredient. + + METHODS collect_orders + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + CHANGING ct_demand TYPE tt_demand. + + METHODS collect_demand + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rt_demand) TYPE tt_demand. + + METHODS to_master + IMPORTING it_catalogue TYPE {{p}}if_canteen_plan=>tt_master + RETURNING VALUE(rt_master) TYPE tt_master. + + METHODS packs_needed + IMPORTING iv_quantity TYPE {{p}}if_canteen_plan=>ty_quantity + iv_pack_size TYPE {{p}}if_canteen_plan=>ty_quantity + RETURNING VALUE(rv_packs) TYPE i. +ENDCLASS. + + +CLASS {{p}}canteen_planner IMPLEMENTATION. + METHOD {{p}}if_canteen_plan~merge_orders. + DATA(lt_demand) = collect_demand( it_orders_a ). + collect_orders( EXPORTING it_orders = it_orders_b + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~pack_requirement. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + DATA ls_master TYPE {{p}}if_canteen_plan=>ty_master. + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + CONTINUE. + ENDIF. + ls_master = lt_master[ ingredient = ls_demand-ingredient ]. + INSERT VALUE #( ingredient = ls_demand-ingredient + packs = packs_needed( iv_quantity = ls_demand-quantity + iv_pack_size = ls_master-pack_size ) ) + INTO TABLE rt_packs. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~missing_ingredients. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + APPEND ls_demand-ingredient TO rt_missing. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF ls_order-quantity <= 0. + CONTINUE. + ENDIF. + READ TABLE ct_demand ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY ingredient = ls_order-ingredient. + IF sy-subrc = 0. + -quantity = -quantity + ls_order-quantity. + ELSE. + INSERT VALUE #( ingredient = ls_order-ingredient + quantity = ls_order-quantity ) INTO TABLE ct_demand. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_demand. + DATA(lt_demand) = VALUE tt_demand( ). + collect_orders( EXPORTING it_orders = it_orders + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD to_master. + LOOP AT it_catalogue INTO DATA(ls_catalogue). + INSERT VALUE #( ingredient = ls_catalogue-ingredient + pack_size = ls_catalogue-pack_size ) INTO TABLE rt_master. + ENDLOOP. + ENDMETHOD. + + METHOD packs_needed. + rv_packs = iv_quantity DIV iv_pack_size. + IF iv_quantity MOD iv_pack_size = 0. + rv_packs = rv_packs + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/faulty/m4_canteen_planner.clas.abap b/tasks_gen/eval/G0118/faulty/m4_canteen_planner.clas.abap new file mode 100644 index 0000000..8a1340e --- /dev/null +++ b/tasks_gen/eval/G0118/faulty/m4_canteen_planner.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}canteen_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_canteen_plan. + + PRIVATE SECTION. + TYPES tt_demand TYPE SORTED TABLE OF {{p}}if_canteen_plan=>ty_order + WITH UNIQUE KEY ingredient. + TYPES tt_master TYPE HASHED TABLE OF {{p}}if_canteen_plan=>ty_master + WITH UNIQUE KEY ingredient. + + METHODS collect_orders + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + CHANGING ct_demand TYPE tt_demand. + + METHODS collect_demand + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rt_demand) TYPE tt_demand. + + METHODS to_master + IMPORTING it_catalogue TYPE {{p}}if_canteen_plan=>tt_master + RETURNING VALUE(rt_master) TYPE tt_master. + + METHODS packs_needed + IMPORTING iv_quantity TYPE {{p}}if_canteen_plan=>ty_quantity + iv_pack_size TYPE {{p}}if_canteen_plan=>ty_quantity + RETURNING VALUE(rv_packs) TYPE i. +ENDCLASS. + + +CLASS {{p}}canteen_planner IMPLEMENTATION. + METHOD {{p}}if_canteen_plan~merge_orders. + DATA(lt_demand) = collect_demand( it_orders_a ). + collect_orders( EXPORTING it_orders = it_orders_b + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~pack_requirement. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + DATA ls_master TYPE {{p}}if_canteen_plan=>ty_master. + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + CONTINUE. + ENDIF. + ls_master = lt_master[ ingredient = ls_demand-ingredient ]. + INSERT VALUE #( ingredient = ls_demand-ingredient + packs = packs_needed( iv_quantity = ls_demand-quantity + iv_pack_size = ls_master-pack_size ) ) + INTO TABLE rt_packs. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~missing_ingredients. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + APPEND ls_demand-ingredient TO rt_missing. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF ls_order-quantity <= 0. + CONTINUE. + ENDIF. + READ TABLE ct_demand ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY ingredient = ls_order-ingredient. + IF sy-subrc = 0. + -quantity = -quantity + ls_order-quantity. + ELSE. + INSERT VALUE #( ingredient = ls_order-ingredient + quantity = ls_order-quantity ) INTO TABLE ct_demand. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_demand. + DATA(lt_demand) = VALUE tt_demand( ). + collect_orders( EXPORTING it_orders = it_orders + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD to_master. + LOOP AT it_catalogue INTO DATA(ls_catalogue). + INSERT VALUE #( ingredient = ls_catalogue-ingredient + pack_size = ls_catalogue-pack_size ) INTO TABLE rt_master. + ENDLOOP. + ENDMETHOD. + + METHOD packs_needed. + rv_packs = iv_quantity DIV iv_pack_size. + IF iv_quantity MOD iv_pack_size <> 0. + rv_packs = rv_packs - 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/generation.json b/tasks_gen/eval/G0118/generation.json new file mode 100644 index 0000000..25a44cd --- /dev/null +++ b/tasks_gen/eval/G0118/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G0118", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 98.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/eval/G0118/hidden/canteen_hidden.clas.abap b/tasks_gen/eval/G0118/hidden/canteen_hidden.clas.abap new file mode 100644 index 0000000..160957b --- /dev/null +++ b/tasks_gen/eval/G0118/hidden/canteen_hidden.clas.abap @@ -0,0 +1,231 @@ +CLASS {{p}}canteen_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_canteen_plan. + + METHODS setup. + + METHODS assert_quantity + IMPORTING iv_expected TYPE {{p}}if_canteen_plan=>ty_quantity + iv_actual TYPE {{p}}if_canteen_plan=>ty_quantity. + + METHODS quantity_of + IMPORTING it_demand TYPE {{p}}if_canteen_plan=>tt_order + iv_ingredient TYPE {{p}}if_canteen_plan=>ty_ingredient + RETURNING VALUE(rv_quantity) TYPE {{p}}if_canteen_plan=>ty_quantity. + + METHODS packs_of + IMPORTING it_packs TYPE {{p}}if_canteen_plan=>tt_pack + iv_ingredient TYPE {{p}}if_canteen_plan=>ty_ingredient + RETURNING VALUE(rv_packs) TYPE i. + + METHODS ingredient_list + IMPORTING it_demand TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rv_list) TYPE string. + + METHODS merges_and_sums FOR TESTING. + METHODS result_is_sorted FOR TESTING. + METHODS ignores_non_positive FOR TESTING. + METHODS empty_inputs FOR TESTING. + METHODS duplicate_in_one_list FOR TESTING. + METHODS packs_round_up FOR TESTING. + METHODS packs_skip_unknown FOR TESTING. + METHODS packs_sorted FOR TESTING. + METHODS missing_lists_unknown FOR TESTING. + METHODS missing_none FOR TESTING. +ENDCLASS. + + +CLASS {{p}}canteen_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}canteen_planner( ). + ENDMETHOD. + + METHOD assert_quantity. + cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ). + ENDMETHOD. + + METHOD quantity_of. + LOOP AT it_demand INTO DATA(ls_demand) WHERE ingredient = iv_ingredient. + rv_quantity = rv_quantity + ls_demand-quantity. + ENDLOOP. + ENDMETHOD. + + METHOD packs_of. + LOOP AT it_packs INTO DATA(ls_pack) WHERE ingredient = iv_ingredient. + rv_packs = rv_packs + ls_pack-packs. + ENDLOOP. + ENDMETHOD. + + METHOD ingredient_list. + LOOP AT it_demand INTO DATA(ls_demand). + rv_list = rv_list && ls_demand-ingredient && `|`. + ENDLOOP. + ENDMETHOD. + + METHOD merges_and_sums. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 5 ) + ( ingredient = 'SUGAR' quantity = 2 ) ). + DATA(lt_b) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 3 ) + ( ingredient = 'BUTTER' quantity = 1 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( it_orders_a = lt_a + it_orders_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_demand ) ). + assert_quantity( iv_expected = 8 + iv_actual = quantity_of( it_demand = lt_demand + iv_ingredient = 'FLOUR' ) ). + assert_quantity( iv_expected = 2 + iv_actual = quantity_of( it_demand = lt_demand + iv_ingredient = 'SUGAR' ) ). + assert_quantity( iv_expected = 1 + iv_actual = quantity_of( it_demand = lt_demand + iv_ingredient = 'BUTTER' ) ). + ENDMETHOD. + + METHOD result_is_sorted. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'SUGAR' quantity = 2 ) + ( ingredient = 'FLOUR' quantity = 1 ) ). + DATA(lt_b) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'BUTTER' quantity = 1 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( it_orders_a = lt_a + it_orders_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( exp = `BUTTER|FLOUR|SUGAR|` + act = ingredient_list( lt_demand ) ). + ENDMETHOD. + + METHOD ignores_non_positive. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 5 ) + ( ingredient = 'SUGAR' quantity = 0 ) ). + DATA(lt_b) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'BUTTER' quantity = -2 ) + ( ingredient = 'FLOUR' quantity = 1 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( it_orders_a = lt_a + it_orders_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( exp = `FLOUR|` + act = ingredient_list( lt_demand ) ). + assert_quantity( iv_expected = 6 + iv_actual = quantity_of( it_demand = lt_demand + iv_ingredient = 'FLOUR' ) ). + ENDMETHOD. + + METHOD empty_inputs. + DATA(lt_demand) = mo_cut->merge_orders( + it_orders_a = VALUE {{p}}if_canteen_plan=>tt_order( ) + it_orders_b = VALUE {{p}}if_canteen_plan=>tt_order( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_demand ) ). + ENDMETHOD. + + METHOD duplicate_in_one_list. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 2 ) + ( ingredient = 'FLOUR' quantity = 3 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( + it_orders_a = lt_a + it_orders_b = VALUE {{p}}if_canteen_plan=>tt_order( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_demand ) ). + assert_quantity( iv_expected = 5 + iv_actual = quantity_of( it_demand = lt_demand + iv_ingredient = 'FLOUR' ) ). + ENDMETHOD. + + METHOD packs_round_up. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 25 ) + ( ingredient = 'SUGAR' quantity = 7 ) + ( ingredient = 'BUTTER' quantity = 3 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) + ( ingredient = 'SUGAR' pack_size = 5 ) + ( ingredient = 'BUTTER' pack_size = 1 ) ). + + DATA(lt_packs) = mo_cut->pack_requirement( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_packs ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = packs_of( it_packs = lt_packs + iv_ingredient = 'FLOUR' ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = packs_of( it_packs = lt_packs + iv_ingredient = 'SUGAR' ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = packs_of( it_packs = lt_packs + iv_ingredient = 'BUTTER' ) ). + ENDMETHOD. + + METHOD packs_skip_unknown. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 10 ) + ( ingredient = 'COCOA' quantity = 5 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) ). + + DATA(lt_packs) = mo_cut->pack_requirement( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_packs ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = packs_of( it_packs = lt_packs + iv_ingredient = 'FLOUR' ) ). + ENDMETHOD. + + METHOD packs_sorted. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'SUGAR' quantity = 11 ) + ( ingredient = 'FLOUR' quantity = 21 ) + ( ingredient = 'BUTTER' quantity = 4 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'SUGAR' pack_size = 5 ) + ( ingredient = 'FLOUR' pack_size = 10 ) + ( ingredient = 'BUTTER' pack_size = 1 ) ). + + DATA(lt_packs) = mo_cut->pack_requirement( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + DATA lv_order TYPE string. + LOOP AT lt_packs INTO DATA(ls_pack). + lv_order = lv_order && ls_pack-ingredient && `|`. + ENDLOOP. + cl_abap_unit_assert=>assert_equals( exp = `BUTTER|FLOUR|SUGAR|` act = lv_order ). + ENDMETHOD. + + METHOD missing_lists_unknown. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'SUGAR' quantity = 5 ) + ( ingredient = 'COCOA' quantity = 1 ) + ( ingredient = 'FLOUR' quantity = 2 ) + ( ingredient = 'COCOA' quantity = 3 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) + ( ingredient = 'SUGAR' pack_size = 5 ) ). + + DATA(lt_missing) = mo_cut->missing_ingredients( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_missing ) ). + LOOP AT lt_missing INTO DATA(lv_ingredient). + cl_abap_unit_assert=>assert_equals( exp = 'COCOA' act = lv_ingredient ). + ENDLOOP. + ENDMETHOD. + + METHOD missing_none. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 1 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) ). + + DATA(lt_missing) = mo_cut->missing_ingredients( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_missing ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/mutation.json b/tasks_gen/eval/G0118/mutation.json new file mode 100644 index 0000000..0454f69 --- /dev/null +++ b/tasks_gen/eval/G0118/mutation.json @@ -0,0 +1,62 @@ +{ + "task": "G0118", + "mutants": [ + { + "object": "{{P}}CANTEEN_PLANNER", + "mutant": "line 44: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}CANTEEN_PLANNER", + "mutant": "line 68: <= -> > (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "DUPLICATE_IN_ONE_LIST", + "IGNORES_NON_POSITIVE", + "MERGES_AND_SUMS", + "MISSING_LISTS_UNKNOWN", + "PACKS_ROUND_UP", + "PACKS_SKIP_UNKNOWN", + "PACKS_SORTED", + "RESULT_IS_SORTED" + ] + }, + { + "object": "{{P}}CANTEEN_PLANNER", + "mutant": "line 74: + -> - (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "DUPLICATE_IN_ONE_LIST", + "IGNORES_NON_POSITIVE", + "MERGES_AND_SUMS" + ] + }, + { + "object": "{{P}}CANTEEN_PLANNER", + "mutant": "line 98: <> -> = (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "PACKS_ROUND_UP", + "PACKS_SKIP_UNKNOWN" + ] + }, + { + "object": "{{P}}CANTEEN_PLANNER", + "mutant": "line 99: + -> - (arith)", + "status": "killed", + "hidden": "9/10", + "failed_tests": [ + "PACKS_ROUND_UP" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0118/reference/canteen_planner.clas.abap b/tasks_gen/eval/G0118/reference/canteen_planner.clas.abap new file mode 100644 index 0000000..fd37356 --- /dev/null +++ b/tasks_gen/eval/G0118/reference/canteen_planner.clas.abap @@ -0,0 +1,102 @@ +CLASS {{p}}canteen_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_canteen_plan. + + PRIVATE SECTION. + TYPES tt_demand TYPE SORTED TABLE OF {{p}}if_canteen_plan=>ty_order + WITH UNIQUE KEY ingredient. + TYPES tt_master TYPE HASHED TABLE OF {{p}}if_canteen_plan=>ty_master + WITH UNIQUE KEY ingredient. + + METHODS collect_orders + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + CHANGING ct_demand TYPE tt_demand. + + METHODS collect_demand + IMPORTING it_orders TYPE {{p}}if_canteen_plan=>tt_order + RETURNING VALUE(rt_demand) TYPE tt_demand. + + METHODS to_master + IMPORTING it_catalogue TYPE {{p}}if_canteen_plan=>tt_master + RETURNING VALUE(rt_master) TYPE tt_master. + + METHODS packs_needed + IMPORTING iv_quantity TYPE {{p}}if_canteen_plan=>ty_quantity + iv_pack_size TYPE {{p}}if_canteen_plan=>ty_quantity + RETURNING VALUE(rv_packs) TYPE i. +ENDCLASS. + + +CLASS {{p}}canteen_planner IMPLEMENTATION. + METHOD {{p}}if_canteen_plan~merge_orders. + DATA(lt_demand) = collect_demand( it_orders_a ). + collect_orders( EXPORTING it_orders = it_orders_b + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~pack_requirement. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + DATA ls_master TYPE {{p}}if_canteen_plan=>ty_master. + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + CONTINUE. + ENDIF. + ls_master = lt_master[ ingredient = ls_demand-ingredient ]. + INSERT VALUE #( ingredient = ls_demand-ingredient + packs = packs_needed( iv_quantity = ls_demand-quantity + iv_pack_size = ls_master-pack_size ) ) + INTO TABLE rt_packs. + ENDLOOP. + ENDMETHOD. + + METHOD {{p}}if_canteen_plan~missing_ingredients. + DATA(lt_master) = to_master( it_catalogue ). + DATA(lt_demand) = collect_demand( it_demand ). + + LOOP AT lt_demand INTO DATA(ls_demand). + IF NOT line_exists( lt_master[ ingredient = ls_demand-ingredient ] ). + APPEND ls_demand-ingredient TO rt_missing. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_orders. + LOOP AT it_orders INTO DATA(ls_order). + IF ls_order-quantity <= 0. + CONTINUE. + ENDIF. + READ TABLE ct_demand ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY ingredient = ls_order-ingredient. + IF sy-subrc = 0. + -quantity = -quantity + ls_order-quantity. + ELSE. + INSERT VALUE #( ingredient = ls_order-ingredient + quantity = ls_order-quantity ) INTO TABLE ct_demand. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD collect_demand. + DATA(lt_demand) = VALUE tt_demand( ). + collect_orders( EXPORTING it_orders = it_orders + CHANGING ct_demand = lt_demand ). + rt_demand = lt_demand. + ENDMETHOD. + + METHOD to_master. + LOOP AT it_catalogue INTO DATA(ls_catalogue). + INSERT VALUE #( ingredient = ls_catalogue-ingredient + pack_size = ls_catalogue-pack_size ) INTO TABLE rt_master. + ENDLOOP. + ENDMETHOD. + + METHOD packs_needed. + rv_packs = iv_quantity DIV iv_pack_size. + IF iv_quantity MOD iv_pack_size <> 0. + rv_packs = rv_packs + 1. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/reference/canteen_planner.testclasses.abap b/tasks_gen/eval/G0118/reference/canteen_planner.testclasses.abap new file mode 100644 index 0000000..2280631 --- /dev/null +++ b/tasks_gen/eval/G0118/reference/canteen_planner.testclasses.abap @@ -0,0 +1,73 @@ +CLASS ltc_canteen_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_canteen_plan. + METHODS setup. + METHODS merges_two_lists FOR TESTING. + METHODS sorts_by_ingredient FOR TESTING. + METHODS rounds_packs_up FOR TESTING. + METHODS lists_unknown FOR TESTING. +ENDCLASS. + + +CLASS ltc_canteen_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}canteen_planner( ). + ENDMETHOD. + + METHOD merges_two_lists. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 2 ) ). + DATA(lt_b) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 3 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( it_orders_a = lt_a + it_orders_b = lt_b ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_demand ) ). + DATA lv_expected TYPE {{p}}if_canteen_plan=>ty_quantity. + lv_expected = 5. + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lt_demand[ 1 ]-quantity ). + ENDMETHOD. + + METHOD sorts_by_ingredient. + DATA(lt_a) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'SUGAR' quantity = 1 ) + ( ingredient = 'FLOUR' quantity = 1 ) ). + + DATA(lt_demand) = mo_cut->merge_orders( + it_orders_a = lt_a + it_orders_b = VALUE {{p}}if_canteen_plan=>tt_order( ) ). + + cl_abap_unit_assert=>assert_equals( exp = 'FLOUR' + act = lt_demand[ 1 ]-ingredient ). + cl_abap_unit_assert=>assert_equals( exp = 'SUGAR' + act = lt_demand[ 2 ]-ingredient ). + ENDMETHOD. + + METHOD rounds_packs_up. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'FLOUR' quantity = 25 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) ). + + DATA(lt_packs) = mo_cut->pack_requirement( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_packs ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_packs[ 1 ]-packs ). + ENDMETHOD. + + METHOD lists_unknown. + DATA(lt_demand) = VALUE {{p}}if_canteen_plan=>tt_order( + ( ingredient = 'COCOA' quantity = 1 ) ). + DATA(lt_catalogue) = VALUE {{p}}if_canteen_plan=>tt_master( + ( ingredient = 'FLOUR' pack_size = 10 ) ). + + DATA(lt_missing) = mo_cut->missing_ingredients( it_demand = lt_demand + it_catalogue = lt_catalogue ). + + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_missing ) ). + cl_abap_unit_assert=>assert_equals( exp = 'COCOA' act = lt_missing[ 1 ] ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0118/seed/if_canteen_plan.intf.abap b/tasks_gen/eval/G0118/seed/if_canteen_plan.intf.abap new file mode 100644 index 0000000..aa73866 --- /dev/null +++ b/tasks_gen/eval/G0118/seed/if_canteen_plan.intf.abap @@ -0,0 +1,42 @@ +INTERFACE {{p}}if_canteen_plan PUBLIC. + TYPES ty_ingredient TYPE c LENGTH 10. + TYPES ty_quantity TYPE p LENGTH 13 DECIMALS 3. + + TYPES: + BEGIN OF ty_order, + ingredient TYPE ty_ingredient, + quantity TYPE ty_quantity, + END OF ty_order. + TYPES tt_order TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_master, + ingredient TYPE ty_ingredient, + pack_size TYPE ty_quantity, + END OF ty_master. + TYPES tt_master TYPE STANDARD TABLE OF ty_master WITH EMPTY KEY. + + TYPES: + BEGIN OF ty_pack, + ingredient TYPE ty_ingredient, + packs TYPE i, + END OF ty_pack. + TYPES tt_pack TYPE STANDARD TABLE OF ty_pack WITH EMPTY KEY. + + TYPES tt_ingredient TYPE STANDARD TABLE OF ty_ingredient WITH EMPTY KEY. + + METHODS merge_orders + IMPORTING it_orders_a TYPE tt_order + it_orders_b TYPE tt_order + RETURNING VALUE(rt_demand) TYPE tt_order. + + METHODS pack_requirement + IMPORTING it_demand TYPE tt_order + it_catalogue TYPE tt_master + RETURNING VALUE(rt_packs) TYPE tt_pack. + + METHODS missing_ingredients + IMPORTING it_demand TYPE tt_order + it_catalogue TYPE tt_master + RETURNING VALUE(rt_missing) TYPE tt_ingredient. +ENDINTERFACE. diff --git a/tasks_gen/eval/G0118/spec.md b/tasks_gen/eval/G0118/spec.md new file mode 100644 index 0000000..032229c --- /dev/null +++ b/tasks_gen/eval/G0118/spec.md @@ -0,0 +1,87 @@ +# 1. Goal +A catering company runs two canteens. Each canteen sends a list of ingredient +orders for the next day. The purchasing team needs one demand list for both +canteens, the number of packs to order for each ingredient, and the ingredients +that the ingredient master data does not contain. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_CANTEEN_PLAN exists in package $TMP. It is active. +- The interface declares these data types: + - TY_INGREDIENT: an ingredient ID, character 10. + - TY_QUANTITY: a quantity, packed number with 3 decimals. + - TY_ORDER: a structure with the components INGREDIENT (TYPE TY_INGREDIENT) + and QUANTITY (TYPE TY_QUANTITY). + - TT_ORDER: a table type with the line type TY_ORDER. + - TY_MASTER: a structure with the components INGREDIENT (TYPE TY_INGREDIENT) + and PACK_SIZE (TYPE TY_QUANTITY). + - TT_MASTER: a table type with the line type TY_MASTER. + - TY_PACK: a structure with the components INGREDIENT (TYPE TY_INGREDIENT) + and PACKS (TYPE i). + - TT_PACK: a table type with the line type TY_PACK. + - TT_INGREDIENT: a table type with the line type TY_INGREDIENT. +- The interface declares these methods: + - MERGE_ORDERS: importing IT_ORDERS_A and IT_ORDERS_B (TYPE TT_ORDER), + returning RT_DEMAND (TYPE TT_ORDER). + - PACK_REQUIREMENT: importing IT_DEMAND (TYPE TT_ORDER) and IT_CATALOGUE + (TYPE TT_MASTER), returning RT_PACKS (TYPE TT_PACK). + - MISSING_INGREDIENTS: importing IT_DEMAND (TYPE TT_ORDER) and IT_CATALOGUE + (TYPE TT_MASTER), returning RT_MISSING (TYPE TT_INGREDIENT). +- An order list is a table of TY_ORDER. A line has an ingredient ID and a + quantity. +- The ingredient master data is a table of TY_MASTER. A line has an ingredient + ID and the pack size of one pack of this ingredient. + +# 4. Contract +- Create the class {{P}}CANTEEN_PLANNER in package $TMP. +- The class is public and final and has a public constructor without parameters. +- The class implements {{P}}IF_CANTEEN_PLAN. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. + +MERGE_ORDERS +1. Read the two order lists. Ignore every line whose quantity is zero or less. +2. The result contains one line per ingredient that occurs in at least one of + the two order lists after rule 1. +3. The quantity of an ingredient in the result is the sum of all its quantities + in both order lists. +4. The result is sorted in ascending order of the ingredient ID. +5. If no line remains after rule 1, the result is empty. + +PACK_REQUIREMENT +6. If an ingredient occurs more than once in the demand list, add its quantities + first. +7. The pack size of an ingredient is always greater than zero. +8. For each ingredient of the demand list, look for the ingredient in the + catalogue. +9. If the catalogue contains the ingredient, the number of packs is the demand + quantity divided by the pack size, rounded up to the next whole number. +10. The result contains one line per demand ingredient that the catalogue + contains. The line has the ingredient ID and the number of packs. +11. The result is sorted in ascending order of the ingredient ID. +12. If the demand list is empty, the result is empty. + +MISSING_INGREDIENTS +13. The result contains the ingredient IDs of the demand list that the + catalogue does not contain. +14. Each ingredient ID occurs at most once in the result. +15. The result is sorted in ascending order of the ingredient ID. +16. If the catalogue contains every ingredient of the demand list, the result + is empty. + +The catalogue contains each ingredient at most once. + +# 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_CANTEEN_PLAN. + +# 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/eval/G0118/task.json b/tasks_gen/eval/G0118/task.json new file mode 100644 index 0000000..ebefc53 --- /dev/null +++ b/tasks_gen/eval/G0118/task.json @@ -0,0 +1,52 @@ +{ + "id": "G0118", + "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_CANTEEN_PLAN", + "file": "seed/if_canteen_plan.intf.abap", + "description": "Contract of the canteen demand planner: data types and the three methods" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CANTEEN_PLANNER", + "implements": "{{P}}IF_CANTEEN_PLAN" + } + ], + "out_of_scope": [ + "{{P}}IF_CANTEEN_PLAN" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}CANTEEN_HIDDEN", + "file": "hidden/canteen_hidden.clas.abap", + "description": "Hidden ABAP Unit tests for the canteen demand planner" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CANTEEN_PLANNER", + "file": "reference/canteen_planner.clas.abap", + "description": "Canteen demand planner with sorted demand table and hashed catalogue lookup", + "testclasses_file": "reference/canteen_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0119/faulty/m0_workload_planner.clas.abap b/tasks_gen/eval/G0119/faulty/m0_workload_planner.clas.abap new file mode 100644 index 0000000..540e566 --- /dev/null +++ b/tasks_gen/eval/G0119/faulty/m0_workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours <= 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 1 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours + is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_true + AND ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/faulty/m1_workload_planner.clas.abap b/tasks_gen/eval/G0119/faulty/m1_workload_planner.clas.abap new file mode 100644 index 0000000..5fd04f3 --- /dev/null +++ b/tasks_gen/eval/G0119/faulty/m1_workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours > 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 2 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours + is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_true + AND ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/faulty/m2_workload_planner.clas.abap b/tasks_gen/eval/G0119/faulty/m2_workload_planner.clas.abap new file mode 100644 index 0000000..a2d0511 --- /dev/null +++ b/tasks_gen/eval/G0119/faulty/m2_workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours > 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 1 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours - is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_true + AND ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/faulty/m3_workload_planner.clas.abap b/tasks_gen/eval/G0119/faulty/m3_workload_planner.clas.abap new file mode 100644 index 0000000..93f3fd5 --- /dev/null +++ b/tasks_gen/eval/G0119/faulty/m3_workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours > 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 1 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours + is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_false + AND ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/faulty/m4_workload_planner.clas.abap b/tasks_gen/eval/G0119/faulty/m4_workload_planner.clas.abap new file mode 100644 index 0000000..a250c1a --- /dev/null +++ b/tasks_gen/eval/G0119/faulty/m4_workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours > 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 1 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours + is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_true + OR ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/generation.json b/tasks_gen/eval/G0119/generation.json new file mode 100644 index 0000000..2f58635 --- /dev/null +++ b/tasks_gen/eval/G0119/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0119", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0119/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0119/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..80bb2da --- /dev/null +++ b/tasks_gen/eval/G0119/hidden/t02_hidden.clas.abap @@ -0,0 +1,175 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES: + ty_mechanic TYPE c LENGTH 8, + ty_date TYPE c LENGTH 8. + + DATA mo_cut TYPE REF TO {{p}}workload_planner. + + METHODS setup. + METHODS empty_input FOR TESTING. + METHODS single_job FOR TESTING. + METHODS same_mechanic_aggregated FOR TESTING. + METHODS different_mechanics_sorted FOR TESTING. + METHODS non_positive_hours_ignored FOR TESTING. + METHODS empty_mechanic_ignored FOR TESTING. + METHODS empty_job_id_ignored FOR TESTING. + METHODS earliest_date_wins FOR TESTING. + METHODS unset_dates_ignored FOR TESTING. + METHODS all_dates_unset FOR TESTING. + METHODS all_jobs_invalid FOR TESTING. + + METHODS add_job + IMPORTING iv_mechanic TYPE ty_mechanic + iv_hours TYPE i + iv_date TYPE ty_date + CHANGING ct_jobs TYPE {{p}}workload_planner=>tt_job. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}workload_planner( ). + ENDMETHOD. + + METHOD add_job. + DATA lv_id TYPE c LENGTH 10. + lv_id = lines( ct_jobs ) + 1. + INSERT VALUE #( job_id = lv_id + mechanic = iv_mechanic + work_hours = iv_hours + start_date = iv_date ) + INTO TABLE ct_jobs. + ENDMETHOD. + + METHOD empty_input. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ). + ENDMETHOD. + + METHOD single_job. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ). + cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD same_mechanic_aggregated. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 6 iv_date = '20250112' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_hours ). + cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD different_mechanics_sorted. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M3' iv_hours = 2 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 3 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M2' iv_hours = 5 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ). + cl_abap_unit_assert=>assert_equals( exp = 'M2' act = lt_result[ 2 ]-mechanic ). + cl_abap_unit_assert=>assert_equals( exp = 'M3' act = lt_result[ 3 ]-mechanic ). + ENDMETHOD. + + METHOD non_positive_hours_ignored. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250111' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = -3 iv_date = '20250112' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ). + ENDMETHOD. + + METHOD empty_mechanic_ignored. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + DATA lv_empty TYPE ty_mechanic. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 7 iv_date = '20250111' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ). + ENDMETHOD. + + METHOD empty_job_id_ignored. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + INSERT VALUE #( mechanic = 'M1' + work_hours = 7 + start_date = '20250111' ) + INTO TABLE lt_jobs. + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ). + ENDMETHOD. + + METHOD earliest_date_wins. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250120' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250105' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250115' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = '20250105' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD unset_dates_ignored. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + DATA lv_unset TYPE ty_date. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = lv_unset + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '00000000' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250115' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = '20250115' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD all_dates_unset. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '00000000' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 2 iv_date = '00000000' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_initial( act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD all_jobs_invalid. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + DATA lv_empty TYPE ty_mechanic. + add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/mutation.json b/tasks_gen/eval/G0119/mutation.json new file mode 100644 index 0000000..926f0bc --- /dev/null +++ b/tasks_gen/eval/G0119/mutation.json @@ -0,0 +1,69 @@ +{ + "task": "G0119", + "mutants": [ + { + "object": "{{P}}WORKLOAD_PLANNER", + "mutant": "line 58: > -> <= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "ALL_DATES_UNSET", + "ALL_JOBS_INVALID", + "DIFFERENT_MECHANICS_SORTED", + "EARLIEST_DATE_WINS", + "EMPTY_JOB_ID_IGNORED", + "EMPTY_MECHANIC_IGNORED", + "NON_POSITIVE_HOURS_IGNORED", + "SAME_MECHANIC_AGGREGATED", + "SINGLE_JOB", + "UNSET_DATES_IGNORED" + ] + }, + { + "object": "{{P}}WORKLOAD_PLANNER", + "mutant": "line 65: 1 -> 2 (const)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "NON_POSITIVE_HOURS_IGNORED", + "SAME_MECHANIC_AGGREGATED", + "SINGLE_JOB" + ] + }, + { + "object": "{{P}}WORKLOAD_PLANNER", + "mutant": "line 73: + -> - (arith)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "SAME_MECHANIC_AGGREGATED" + ] + }, + { + "object": "{{P}}WORKLOAD_PLANNER", + "mutant": "line 74: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "ALL_DATES_UNSET", + "EARLIEST_DATE_WINS", + "UNSET_DATES_IGNORED" + ] + }, + { + "object": "{{P}}WORKLOAD_PLANNER", + "mutant": "line 75: AND -> OR (logic)", + "status": "killed", + "hidden": "8/11", + "failed_tests": [ + "ALL_DATES_UNSET", + "EARLIEST_DATE_WINS", + "SAME_MECHANIC_AGGREGATED" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0119/reference/workload_planner.clas.abap b/tasks_gen/eval/G0119/reference/workload_planner.clas.abap new file mode 100644 index 0000000..9d1c11c --- /dev/null +++ b/tasks_gen/eval/G0119/reference/workload_planner.clas.abap @@ -0,0 +1,84 @@ +CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_job, + job_id TYPE c LENGTH 10, + mechanic TYPE c LENGTH 8, + work_hours TYPE i, + start_date TYPE c LENGTH 8, + END OF ty_job, + tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY, + BEGIN OF ty_workload, + mechanic TYPE c LENGTH 8, + job_count TYPE i, + total_hours TYPE i, + first_date TYPE c LENGTH 8, + END OF ty_workload, + tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY. + + METHODS summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. + + PRIVATE SECTION. + TYPES: + ty_date TYPE c LENGTH 8, + tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic. + + METHODS is_relevant + IMPORTING is_job TYPE ty_job + RETURNING VALUE(rv_relevant) TYPE abap_bool. + + METHODS add_job + IMPORTING is_job TYPE ty_job + CHANGING ct_workload TYPE tt_workload_hashed. + + METHODS is_date_set + IMPORTING iv_date TYPE ty_date + RETURNING VALUE(rv_set) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}workload_planner IMPLEMENTATION. + METHOD summarize. + DATA lt_workload TYPE tt_workload_hashed. + LOOP AT it_jobs INTO DATA(ls_job). + IF is_relevant( ls_job ) = abap_true. + add_job( EXPORTING is_job = ls_job + CHANGING ct_workload = lt_workload ). + ENDIF. + ENDLOOP. + rt_workload = lt_workload. + SORT rt_workload BY mechanic. + ENDMETHOD. + + METHOD is_relevant. + rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL + AND is_job-mechanic IS NOT INITIAL + AND is_job-work_hours > 0 ). + ENDMETHOD. + + METHOD add_job. + ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(). + IF sy-subrc <> 0. + INSERT VALUE #( mechanic = is_job-mechanic + job_count = 1 + total_hours = is_job-work_hours + first_date = COND #( WHEN is_date_set( is_job-start_date ) + THEN is_job-start_date ) ) + INTO TABLE ct_workload ASSIGNING . + RETURN. + ENDIF. + -job_count = -job_count + 1. + -total_hours = -total_hours + is_job-work_hours. + IF is_date_set( is_job-start_date ) = abap_true + AND ( -first_date IS INITIAL + OR is_job-start_date < -first_date ). + -first_date = is_job-start_date. + ENDIF. + ENDMETHOD. + + METHOD is_date_set. + rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/reference/workload_planner.testclasses.abap b/tasks_gen/eval/G0119/reference/workload_planner.testclasses.abap new file mode 100644 index 0000000..bcc512f --- /dev/null +++ b/tasks_gen/eval/G0119/reference/workload_planner.testclasses.abap @@ -0,0 +1,85 @@ +CLASS ltc_workload_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES: + ty_mechanic TYPE c LENGTH 8, + ty_date TYPE c LENGTH 8. + + DATA mo_cut TYPE REF TO {{p}}workload_planner. + + METHODS setup. + METHODS empty_input FOR TESTING. + METHODS one_mechanic FOR TESTING. + METHODS sorted_by_mechanic FOR TESTING. + METHODS ignores_invalid_jobs FOR TESTING. + METHODS keeps_earliest_date FOR TESTING. + + METHODS add_job + IMPORTING iv_mechanic TYPE ty_mechanic + iv_hours TYPE i + iv_date TYPE ty_date + CHANGING ct_jobs TYPE {{p}}workload_planner=>tt_job. +ENDCLASS. + + +CLASS ltc_workload_planner IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}workload_planner( ). + ENDMETHOD. + + METHOD add_job. + INSERT VALUE #( job_id = 'JOB' + mechanic = iv_mechanic + work_hours = iv_hours + start_date = iv_date ) + INTO TABLE ct_jobs. + ENDMETHOD. + + METHOD empty_input. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ). + ENDMETHOD. + + METHOD one_mechanic. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 6 iv_date = '20250112' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-job_count ). + cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_hours ). + cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. + + METHOD sorted_by_mechanic. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M2' iv_hours = 1 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ). + cl_abap_unit_assert=>assert_equals( exp = 'M2' act = lt_result[ 2 ]-mechanic ). + ENDMETHOD. + + METHOD ignores_invalid_jobs. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + DATA lv_empty TYPE ty_mechanic. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 5 iv_date = '20250110' + CHANGING ct_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ). + ENDMETHOD. + + METHOD keeps_earliest_date. + DATA lt_jobs TYPE {{p}}workload_planner=>tt_job. + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250201' + CHANGING ct_jobs = lt_jobs ). + add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250101' + CHANGING ct_jobs = lt_jobs ). + DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ). + cl_abap_unit_assert=>assert_equals( exp = '20250101' act = lt_result[ 1 ]-first_date ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0119/spec.md b/tasks_gen/eval/G0119/spec.md new file mode 100644 index 0000000..796c271 --- /dev/null +++ b/tasks_gen/eval/G0119/spec.md @@ -0,0 +1,54 @@ +# 1. Goal +A vehicle workshop plans the workload of its mechanics. A planning program calls +one service of the class {{P}}WORKLOAD_PLANNER. The service groups the open +repair jobs by mechanic and returns one row per mechanic. + +# 2. Open questions +None. + +# 3. Context +- The workshop receives the open repair jobs from an external planning system. + The start date arrives as a character field in the format YYYYMMDD. +- The class is new. No other object is in scope. + +# 4. Contract +- Create the class {{P}}WORKLOAD_PLANNER in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - ty_job with the components job_id (c LENGTH 10), mechanic (c LENGTH 8), + work_hours (i), start_date (c LENGTH 8). + - tt_job: a standard table of ty_job with an empty key. + - ty_workload with the components mechanic (c LENGTH 8), job_count (i), + total_hours (i), first_date (c LENGTH 8). + - tt_workload: a standard table of ty_workload with an empty key. +- The class has this public method: + summarize + IMPORTING it_jobs TYPE tt_job + RETURNING VALUE(rt_workload) TYPE tt_workload. +- Do not add other public methods. + +# 5. Business rules +1. A job is relevant if the job ID is not empty, the mechanic is not empty, and + the work hours are greater than zero. Ignore all other jobs. +2. Group the relevant jobs by mechanic. +3. The result has one row per mechanic: + - mechanic: the mechanic. + - job_count: the number of relevant jobs of this mechanic. + - total_hours: the sum of the work hours of these jobs. + - first_date: the earliest start date of these jobs. +4. A start date is set if it is not empty and if it is not 00000000. When you + determine the earliest date, consider only set dates. If no job of a mechanic + has a set date, first_date is empty. +5. Sort the result by mechanic in ascending order. +6. If no relevant job exists, return an empty table. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change objects outside package $TMP. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/eval/G0119/task.json b/tasks_gen/eval/G0119/task.json new file mode 100644 index 0000000..186a2ad --- /dev/null +++ b/tasks_gen/eval/G0119/task.json @@ -0,0 +1,42 @@ +{ + "id": "G0119", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}WORKLOAD_PLANNER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}WORKLOAD_PLANNER", + "file": "reference/workload_planner.clas.abap", + "description": "Workload planner", + "testclasses_file": "reference/workload_planner.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "nested_loops", + "internal_table_access" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0120/faulty/m0_meter_consumption.clas.abap b/tasks_gen/eval/G0120/faulty/m0_meter_consumption.clas.abap new file mode 100644 index 0000000..ed553e9 --- /dev/null +++ b/tasks_gen/eval/G0120/faulty/m0_meter_consumption.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_meter_reading. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_meter, + meter_id TYPE {{p}}if_meter_reading=>ty_meter_id, + opening_date TYPE {{p}}if_meter_reading=>ty_reading_date, + opening_value TYPE {{p}}if_meter_reading=>ty_value, + closing_date TYPE {{p}}if_meter_reading=>ty_reading_date, + closing_value TYPE {{p}}if_meter_reading=>ty_value, + END OF ty_meter, + tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id. + + METHODS collect + IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings + RETURNING VALUE(rt_meter) TYPE tt_meter. + METHODS to_result + IMPORTING it_meter TYPE tt_meter + RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result. +ENDCLASS. + + +CLASS {{p}}meter_consumption IMPLEMENTATION. + METHOD {{p}}if_meter_reading~calculate. + rt_result = to_result( collect( it_readings ) ). + ENDMETHOD. + + METHOD collect. + LOOP AT it_readings INTO DATA(ls_reading). + IF ls_reading-reading_value >= 0. + CONTINUE. + ENDIF. + READ TABLE rt_meter ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY meter_id = ls_reading-meter_id. + IF sy-subrc <> 0. + INSERT VALUE #( meter_id = ls_reading-meter_id + opening_date = ls_reading-reading_date + opening_value = ls_reading-reading_value + closing_date = ls_reading-reading_date + closing_value = ls_reading-reading_value ) + INTO TABLE rt_meter ASSIGNING . + ENDIF. + IF ls_reading-reading_date < -opening_date + OR ( ls_reading-reading_date = -opening_date + AND ls_reading-reading_value > -opening_value ). + -opening_date = ls_reading-reading_date. + -opening_value = ls_reading-reading_value. + ENDIF. + IF ls_reading-reading_date > -closing_date + OR ( ls_reading-reading_date = -closing_date + AND ls_reading-reading_value > -closing_value ). + -closing_date = ls_reading-reading_date. + -closing_value = ls_reading-reading_value. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD to_result. + LOOP AT it_meter ASSIGNING FIELD-SYMBOL(). + DATA(lv_consumption) = -closing_value - -opening_value. + IF lv_consumption < 0. + lv_consumption = 0. + ENDIF. + INSERT VALUE #( meter_id = -meter_id + opening_value = -opening_value + closing_value = -closing_value + consumption = lv_consumption ) + INTO TABLE rt_result. + ENDLOOP. + SORT rt_result BY meter_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/faulty/m1_meter_consumption.clas.abap b/tasks_gen/eval/G0120/faulty/m1_meter_consumption.clas.abap new file mode 100644 index 0000000..3e70507 --- /dev/null +++ b/tasks_gen/eval/G0120/faulty/m1_meter_consumption.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_meter_reading. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_meter, + meter_id TYPE {{p}}if_meter_reading=>ty_meter_id, + opening_date TYPE {{p}}if_meter_reading=>ty_reading_date, + opening_value TYPE {{p}}if_meter_reading=>ty_value, + closing_date TYPE {{p}}if_meter_reading=>ty_reading_date, + closing_value TYPE {{p}}if_meter_reading=>ty_value, + END OF ty_meter, + tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id. + + METHODS collect + IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings + RETURNING VALUE(rt_meter) TYPE tt_meter. + METHODS to_result + IMPORTING it_meter TYPE tt_meter + RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result. +ENDCLASS. + + +CLASS {{p}}meter_consumption IMPLEMENTATION. + METHOD {{p}}if_meter_reading~calculate. + rt_result = to_result( collect( it_readings ) ). + ENDMETHOD. + + METHOD collect. + LOOP AT it_readings INTO DATA(ls_reading). + IF ls_reading-reading_value < 0. + CONTINUE. + ENDIF. + READ TABLE rt_meter ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY meter_id = ls_reading-meter_id. + IF sy-subrc <> 0. + INSERT VALUE #( meter_id = ls_reading-meter_id + opening_date = ls_reading-reading_date + opening_value = ls_reading-reading_value + closing_date = ls_reading-reading_date + closing_value = ls_reading-reading_value ) + INTO TABLE rt_meter ASSIGNING . + ENDIF. + IF ls_reading-reading_date < -opening_date + OR ( ls_reading-reading_date <> -opening_date + AND ls_reading-reading_value > -opening_value ). + -opening_date = ls_reading-reading_date. + -opening_value = ls_reading-reading_value. + ENDIF. + IF ls_reading-reading_date > -closing_date + OR ( ls_reading-reading_date = -closing_date + AND ls_reading-reading_value > -closing_value ). + -closing_date = ls_reading-reading_date. + -closing_value = ls_reading-reading_value. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD to_result. + LOOP AT it_meter ASSIGNING FIELD-SYMBOL(). + DATA(lv_consumption) = -closing_value - -opening_value. + IF lv_consumption < 0. + lv_consumption = 0. + ENDIF. + INSERT VALUE #( meter_id = -meter_id + opening_value = -opening_value + closing_value = -closing_value + consumption = lv_consumption ) + INTO TABLE rt_result. + ENDLOOP. + SORT rt_result BY meter_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/faulty/m2_meter_consumption.clas.abap b/tasks_gen/eval/G0120/faulty/m2_meter_consumption.clas.abap new file mode 100644 index 0000000..001a410 --- /dev/null +++ b/tasks_gen/eval/G0120/faulty/m2_meter_consumption.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_meter_reading. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_meter, + meter_id TYPE {{p}}if_meter_reading=>ty_meter_id, + opening_date TYPE {{p}}if_meter_reading=>ty_reading_date, + opening_value TYPE {{p}}if_meter_reading=>ty_value, + closing_date TYPE {{p}}if_meter_reading=>ty_reading_date, + closing_value TYPE {{p}}if_meter_reading=>ty_value, + END OF ty_meter, + tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id. + + METHODS collect + IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings + RETURNING VALUE(rt_meter) TYPE tt_meter. + METHODS to_result + IMPORTING it_meter TYPE tt_meter + RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result. +ENDCLASS. + + +CLASS {{p}}meter_consumption IMPLEMENTATION. + METHOD {{p}}if_meter_reading~calculate. + rt_result = to_result( collect( it_readings ) ). + ENDMETHOD. + + METHOD collect. + LOOP AT it_readings INTO DATA(ls_reading). + IF ls_reading-reading_value < 0. + CONTINUE. + ENDIF. + READ TABLE rt_meter ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY meter_id = ls_reading-meter_id. + IF sy-subrc <> 0. + INSERT VALUE #( meter_id = ls_reading-meter_id + opening_date = ls_reading-reading_date + opening_value = ls_reading-reading_value + closing_date = ls_reading-reading_date + closing_value = ls_reading-reading_value ) + INTO TABLE rt_meter ASSIGNING . + ENDIF. + IF ls_reading-reading_date < -opening_date + OR ( ls_reading-reading_date = -opening_date + AND ls_reading-reading_value > -opening_value ). + -opening_date = ls_reading-reading_date. + -opening_value = ls_reading-reading_value. + ENDIF. + IF ls_reading-reading_date > -closing_date + AND ( ls_reading-reading_date = -closing_date + AND ls_reading-reading_value > -closing_value ). + -closing_date = ls_reading-reading_date. + -closing_value = ls_reading-reading_value. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD to_result. + LOOP AT it_meter ASSIGNING FIELD-SYMBOL(). + DATA(lv_consumption) = -closing_value - -opening_value. + IF lv_consumption < 0. + lv_consumption = 0. + ENDIF. + INSERT VALUE #( meter_id = -meter_id + opening_value = -opening_value + closing_value = -closing_value + consumption = lv_consumption ) + INTO TABLE rt_result. + ENDLOOP. + SORT rt_result BY meter_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/faulty/m4_meter_consumption.clas.abap b/tasks_gen/eval/G0120/faulty/m4_meter_consumption.clas.abap new file mode 100644 index 0000000..79feb47 --- /dev/null +++ b/tasks_gen/eval/G0120/faulty/m4_meter_consumption.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_meter_reading. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_meter, + meter_id TYPE {{p}}if_meter_reading=>ty_meter_id, + opening_date TYPE {{p}}if_meter_reading=>ty_reading_date, + opening_value TYPE {{p}}if_meter_reading=>ty_value, + closing_date TYPE {{p}}if_meter_reading=>ty_reading_date, + closing_value TYPE {{p}}if_meter_reading=>ty_value, + END OF ty_meter, + tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id. + + METHODS collect + IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings + RETURNING VALUE(rt_meter) TYPE tt_meter. + METHODS to_result + IMPORTING it_meter TYPE tt_meter + RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result. +ENDCLASS. + + +CLASS {{p}}meter_consumption IMPLEMENTATION. + METHOD {{p}}if_meter_reading~calculate. + rt_result = to_result( collect( it_readings ) ). + ENDMETHOD. + + METHOD collect. + LOOP AT it_readings INTO DATA(ls_reading). + IF ls_reading-reading_value < 0. + CONTINUE. + ENDIF. + READ TABLE rt_meter ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY meter_id = ls_reading-meter_id. + IF sy-subrc <> 0. + INSERT VALUE #( meter_id = ls_reading-meter_id + opening_date = ls_reading-reading_date + opening_value = ls_reading-reading_value + closing_date = ls_reading-reading_date + closing_value = ls_reading-reading_value ) + INTO TABLE rt_meter ASSIGNING . + ENDIF. + IF ls_reading-reading_date < -opening_date + OR ( ls_reading-reading_date = -opening_date + AND ls_reading-reading_value > -opening_value ). + -opening_date = ls_reading-reading_date. + -opening_value = ls_reading-reading_value. + ENDIF. + IF ls_reading-reading_date > -closing_date + OR ( ls_reading-reading_date = -closing_date + AND ls_reading-reading_value <= -closing_value ). + -closing_date = ls_reading-reading_date. + -closing_value = ls_reading-reading_value. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD to_result. + LOOP AT it_meter ASSIGNING FIELD-SYMBOL(). + DATA(lv_consumption) = -closing_value - -opening_value. + IF lv_consumption < 0. + lv_consumption = 0. + ENDIF. + INSERT VALUE #( meter_id = -meter_id + opening_value = -opening_value + closing_value = -closing_value + consumption = lv_consumption ) + INTO TABLE rt_result. + ENDLOOP. + SORT rt_result BY meter_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/generation.json b/tasks_gen/eval/G0120/generation.json new file mode 100644 index 0000000..14eb466 --- /dev/null +++ b/tasks_gen/eval/G0120/generation.json @@ -0,0 +1,19 @@ +{ + "id": "G0120", + "pool": "eval", + "object_type": "CLAS", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 4, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0120/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0120/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..a2b2aeb --- /dev/null +++ b/tasks_gen/eval/G0120/hidden/t02_hidden.clas.abap @@ -0,0 +1,151 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_meter_reading. + METHODS setup. + METHODS assert_row + IMPORTING iv_index TYPE i + iv_meter_id TYPE {{p}}if_meter_reading=>ty_meter_id + iv_opening TYPE {{p}}if_meter_reading=>ty_value + iv_closing TYPE {{p}}if_meter_reading=>ty_value + iv_consumption TYPE {{p}}if_meter_reading=>ty_value + it_result TYPE {{p}}if_meter_reading=>tt_result. + METHODS empty_input FOR TESTING. + METHODS single_reading FOR TESTING. + METHODS two_readings FOR TESTING. + METHODS negative_ignored FOR TESTING. + METHODS only_negative FOR TESTING. + METHODS zero_reading FOR TESTING. + METHODS duplicate_date FOR TESTING. + METHODS sorted_by_meter FOR TESTING. + METHODS rollover FOR TESTING. + METHODS date_order FOR TESTING. + METHODS several_meters FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}meter_consumption( ). + ENDMETHOD. + + METHOD assert_row. + DATA(ls_row) = it_result[ iv_index ]. + cl_abap_unit_assert=>assert_equals( exp = iv_meter_id act = ls_row-meter_id msg = 'meter id' ). + cl_abap_unit_assert=>assert_equals( exp = iv_opening act = ls_row-opening_value msg = 'opening' ). + cl_abap_unit_assert=>assert_equals( exp = iv_closing act = ls_row-closing_value msg = 'closing' ). + cl_abap_unit_assert=>assert_equals( exp = iv_consumption act = ls_row-consumption msg = 'consumption' ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD single_reading. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 100 + iv_consumption = 0 it_result = lt_result ). + ENDMETHOD. + + METHOD two_readings. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 175 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 175 + iv_consumption = 75 it_result = lt_result ). + ENDMETHOD. + + METHOD negative_ignored. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240115' reading_value = -3 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 200 + iv_consumption = 100 it_result = lt_result ). + ENDMETHOD. + + METHOD only_negative. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = -1 ) + ( meter_id = 'M0002' reading_date = '20240110' reading_value = -5 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ). + ENDMETHOD. + + METHOD zero_reading. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 0 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 50 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 0 iv_closing = 50 + iv_consumption = 50 it_result = lt_result ). + ENDMETHOD. + + METHOD duplicate_date. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 120 ) + ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 180 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 120 iv_closing = 200 + iv_consumption = 80 it_result = lt_result ). + ENDMETHOD. + + METHOD sorted_by_meter. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0003' reading_date = '20240110' reading_value = 30 ) + ( meter_id = 'M0001' reading_date = '20240110' reading_value = 10 ) + ( meter_id = 'M0002' reading_date = '20240110' reading_value = 20 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'M0001' act = lt_result[ 1 ]-meter_id ). + cl_abap_unit_assert=>assert_equals( exp = 'M0002' act = lt_result[ 2 ]-meter_id ). + cl_abap_unit_assert=>assert_equals( exp = 'M0003' act = lt_result[ 3 ]-meter_id ). + ENDMETHOD. + + METHOD rollover. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 500 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 300 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 500 iv_closing = 300 + iv_consumption = 0 it_result = lt_result ). + ENDMETHOD. + + METHOD date_order. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) + ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 200 + iv_consumption = 100 it_result = lt_result ). + ENDMETHOD. + + METHOD several_meters. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240310' reading_value = 250 ) + ( meter_id = 'M0002' reading_date = '20240105' reading_value = 40 ) + ( meter_id = 'M0002' reading_date = '20240205' reading_value = 55 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 250 + iv_consumption = 150 it_result = lt_result ). + assert_row( iv_index = 2 iv_meter_id = 'M0002' iv_opening = 40 iv_closing = 55 + iv_consumption = 15 it_result = lt_result ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/mutation.json b/tasks_gen/eval/G0120/mutation.json new file mode 100644 index 0000000..6fbee47 --- /dev/null +++ b/tasks_gen/eval/G0120/mutation.json @@ -0,0 +1,70 @@ +{ + "task": "G0120", + "mutants": [ + { + "object": "{{P}}METER_CONSUMPTION", + "mutant": "line 32: < -> >= (rel)", + "status": "killed", + "hidden": "1/11", + "failed_tests": [ + "DATE_ORDER", + "DUPLICATE_DATE", + "NEGATIVE_IGNORED", + "ONLY_NEGATIVE", + "ROLLOVER", + "SEVERAL_METERS", + "SINGLE_READING", + "SORTED_BY_METER", + "TWO_READINGS", + "ZERO_READING" + ] + }, + { + "object": "{{P}}METER_CONSUMPTION", + "mutant": "line 46: = -> <> (eq)", + "status": "killed", + "hidden": "6/11", + "failed_tests": [ + "DUPLICATE_DATE", + "NEGATIVE_IGNORED", + "SEVERAL_METERS", + "TWO_READINGS", + "ZERO_READING" + ] + }, + { + "object": "{{P}}METER_CONSUMPTION", + "mutant": "line 52: OR -> AND (logic)", + "status": "killed", + "hidden": "5/11", + "failed_tests": [ + "DUPLICATE_DATE", + "NEGATIVE_IGNORED", + "ROLLOVER", + "SEVERAL_METERS", + "TWO_READINGS", + "ZERO_READING" + ] + }, + { + "object": "{{P}}METER_CONSUMPTION", + "mutant": "line 53: AND -> OR (logic)", + "status": "survived", + "hidden": "11/11", + "failed_tests": [] + }, + { + "object": "{{P}}METER_CONSUMPTION", + "mutant": "line 53: > -> <= (rel)", + "status": "killed", + "hidden": "10/11", + "failed_tests": [ + "DUPLICATE_DATE" + ] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0120/reference/meter_consumption.clas.abap b/tasks_gen/eval/G0120/reference/meter_consumption.clas.abap new file mode 100644 index 0000000..74cce4f --- /dev/null +++ b/tasks_gen/eval/G0120/reference/meter_consumption.clas.abap @@ -0,0 +1,74 @@ +CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES {{p}}if_meter_reading. + + PRIVATE SECTION. + TYPES: + BEGIN OF ty_meter, + meter_id TYPE {{p}}if_meter_reading=>ty_meter_id, + opening_date TYPE {{p}}if_meter_reading=>ty_reading_date, + opening_value TYPE {{p}}if_meter_reading=>ty_value, + closing_date TYPE {{p}}if_meter_reading=>ty_reading_date, + closing_value TYPE {{p}}if_meter_reading=>ty_value, + END OF ty_meter, + tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id. + + METHODS collect + IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings + RETURNING VALUE(rt_meter) TYPE tt_meter. + METHODS to_result + IMPORTING it_meter TYPE tt_meter + RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result. +ENDCLASS. + + +CLASS {{p}}meter_consumption IMPLEMENTATION. + METHOD {{p}}if_meter_reading~calculate. + rt_result = to_result( collect( it_readings ) ). + ENDMETHOD. + + METHOD collect. + LOOP AT it_readings INTO DATA(ls_reading). + IF ls_reading-reading_value < 0. + CONTINUE. + ENDIF. + READ TABLE rt_meter ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY meter_id = ls_reading-meter_id. + IF sy-subrc <> 0. + INSERT VALUE #( meter_id = ls_reading-meter_id + opening_date = ls_reading-reading_date + opening_value = ls_reading-reading_value + closing_date = ls_reading-reading_date + closing_value = ls_reading-reading_value ) + INTO TABLE rt_meter ASSIGNING . + ENDIF. + IF ls_reading-reading_date < -opening_date + OR ( ls_reading-reading_date = -opening_date + AND ls_reading-reading_value > -opening_value ). + -opening_date = ls_reading-reading_date. + -opening_value = ls_reading-reading_value. + ENDIF. + IF ls_reading-reading_date > -closing_date + OR ( ls_reading-reading_date = -closing_date + AND ls_reading-reading_value > -closing_value ). + -closing_date = ls_reading-reading_date. + -closing_value = ls_reading-reading_value. + ENDIF. + ENDLOOP. + ENDMETHOD. + + METHOD to_result. + LOOP AT it_meter ASSIGNING FIELD-SYMBOL(). + DATA(lv_consumption) = -closing_value - -opening_value. + IF lv_consumption < 0. + lv_consumption = 0. + ENDIF. + INSERT VALUE #( meter_id = -meter_id + opening_value = -opening_value + closing_value = -closing_value + consumption = lv_consumption ) + INTO TABLE rt_result. + ENDLOOP. + SORT rt_result BY meter_id. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/reference/meter_consumption.testclasses.abap b/tasks_gen/eval/G0120/reference/meter_consumption.testclasses.abap new file mode 100644 index 0000000..2047e02 --- /dev/null +++ b/tasks_gen/eval/G0120/reference/meter_consumption.testclasses.abap @@ -0,0 +1,75 @@ +CLASS ltc_meter_consumption DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}if_meter_reading. + METHODS setup. + METHODS two_readings FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS negative_ignored FOR TESTING. + METHODS duplicate_date FOR TESTING. + METHODS rollover_is_zero FOR TESTING. + METHODS sorted_result FOR TESTING. +ENDCLASS. + + +CLASS ltc_meter_consumption IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}meter_consumption( ). + ENDMETHOD. + + METHOD two_readings. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 175 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_result[ 1 ]-opening_value ). + cl_abap_unit_assert=>assert_equals( exp = 175 act = lt_result[ 1 ]-closing_value ). + cl_abap_unit_assert=>assert_equals( exp = 75 act = lt_result[ 1 ]-consumption ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_initial( act = lt_result ). + ENDMETHOD. + + METHOD negative_ignored. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) + ( meter_id = 'M0001' reading_date = '20240120' reading_value = -7 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_result[ 1 ]-consumption ). + ENDMETHOD. + + METHOD duplicate_date. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 130 ) + ( meter_id = 'M0001' reading_date = '20240110' reading_value = 110 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 210 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 130 act = lt_result[ 1 ]-opening_value ). + cl_abap_unit_assert=>assert_equals( exp = 80 act = lt_result[ 1 ]-consumption ). + ENDMETHOD. + + METHOD rollover_is_zero. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 900 ) + ( meter_id = 'M0001' reading_date = '20240210' reading_value = 100 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_result[ 1 ]-consumption ). + ENDMETHOD. + + METHOD sorted_result. + DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings. + lt_readings = VALUE #( ( meter_id = 'M0002' reading_date = '20240110' reading_value = 20 ) + ( meter_id = 'M0001' reading_date = '20240110' reading_value = 10 ) ). + DATA(lt_result) = mo_cut->calculate( lt_readings ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ). + cl_abap_unit_assert=>assert_equals( exp = 'M0001' act = lt_result[ 1 ]-meter_id ). + cl_abap_unit_assert=>assert_equals( exp = 'M0002' act = lt_result[ 2 ]-meter_id ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0120/seed/if_meter_reading.intf.abap b/tasks_gen/eval/G0120/seed/if_meter_reading.intf.abap new file mode 100644 index 0000000..c8e3600 --- /dev/null +++ b/tasks_gen/eval/G0120/seed/if_meter_reading.intf.abap @@ -0,0 +1,23 @@ +INTERFACE {{p}}if_meter_reading PUBLIC. + TYPES ty_meter_id TYPE c LENGTH 10. + TYPES ty_reading_date TYPE d. + TYPES ty_value TYPE i. + TYPES: + BEGIN OF ty_reading, + meter_id TYPE ty_meter_id, + reading_date TYPE ty_reading_date, + reading_value TYPE ty_value, + END OF ty_reading. + TYPES tt_readings TYPE STANDARD TABLE OF ty_reading WITH DEFAULT KEY. + TYPES: + BEGIN OF ty_result, + meter_id TYPE ty_meter_id, + opening_value TYPE ty_value, + closing_value TYPE ty_value, + consumption TYPE ty_value, + END OF ty_result. + TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY. + METHODS calculate + IMPORTING it_readings TYPE tt_readings + RETURNING VALUE(rt_result) TYPE tt_result. +ENDINTERFACE. diff --git a/tasks_gen/eval/G0120/spec.md b/tasks_gen/eval/G0120/spec.md new file mode 100644 index 0000000..a073af9 --- /dev/null +++ b/tasks_gen/eval/G0120/spec.md @@ -0,0 +1,57 @@ +# 1. Goal +A water utility bills its customers for the water that they use. The billing +program needs one service that calculates the consumption of each water meter +from the readings of a period. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_METER_READING exists in package $TMP. It is active. +- The interface contains these public types: + TY_METER_ID (CHAR 10), TY_READING_DATE (date), TY_VALUE (integer), + TY_READING (METER_ID, READING_DATE, READING_VALUE), + TT_READINGS (a table of TY_READING), + TY_RESULT (METER_ID, OPENING_VALUE, CLOSING_VALUE, CONSUMPTION), + TT_RESULT (a table of TY_RESULT). +- The interface has one method: + CALCULATE IMPORTING it_readings TYPE tt_readings + RETURNING VALUE(rt_result) TYPE tt_result. +- A reading is the meter value in cubic metres that a reader wrote down on a + date. + +# 4. Contract +- Create the class {{P}}METER_CONSUMPTION in package $TMP. +- The class is public, final, and has a public constructor without parameters. +- The class implements {{P}}IF_METER_READING. +- Do not add other public methods. + +# 5. Business rules +The method gets the readings of all meters of one period. The input table can be +unsorted. It can contain more than one reading for the same meter and the same +date. +1. Ignore every reading whose value is less than zero. +2. If two or more readings have the same meter ID and the same date, only the + reading with the highest value counts. Ignore the other readings of that + date. +3. For each meter, the opening value is the value of the earliest date of the + meter, and the closing value is the value of the latest date of the meter. + If a meter has only one reading, the opening value and the closing value are + the value of that reading. +4. The consumption of a meter is the closing value minus the opening value. If + that difference is less than zero, the consumption is zero. +5. The result has one row for each meter with at least one reading that rule 1 + does not ignore. +6. The result is sorted by meter ID in ascending order. +7. If no reading remains after rule 1, the result is empty. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. Method length below 40 statements. No global + variables. No comment that restates the code. +- Out of scope: do not change {{P}}IF_METER_READING. + +# 7. Acceptance +- The class is active and has no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class. diff --git a/tasks_gen/eval/G0120/task.json b/tasks_gen/eval/G0120/task.json new file mode 100644 index 0000000..d9c4b61 --- /dev/null +++ b/tasks_gen/eval/G0120/task.json @@ -0,0 +1,51 @@ +{ + "id": "G0120", + "category": "C", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 12 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_METER_READING", + "file": "seed/if_meter_reading.intf.abap", + "description": "Meter reading contract with the reading types and the calculate method" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}METER_CONSUMPTION", + "implements": "{{P}}IF_METER_READING" + } + ], + "out_of_scope": [ + "{{P}}IF_METER_READING" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests for the meter consumption calculation" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}METER_CONSUMPTION", + "file": "reference/meter_consumption.clas.abap", + "description": "Meter consumption calculator", + "testclasses_file": "reference/meter_consumption.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "internal_table_access" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0121/generation.json b/tasks_gen/eval/G0121/generation.json new file mode 100644 index 0000000..317a517 --- /dev/null +++ b/tasks_gen/eval/G0121/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0121", + "pool": "eval", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "stage": "validate", + "oracle": null, + "null": null + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/eval/G0121/hidden/t14_hidden.clas.abap b/tasks_gen/eval/G0121/hidden/t14_hidden.clas.abap new file mode 100644 index 0000000..d30e45e --- /dev/null +++ b/tasks_gen/eval/G0121/hidden/t14_hidden.clas.abap @@ -0,0 +1,220 @@ +CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_orders TYPE STANDARD TABLE OF {{p}}roasts. + TYPES ty_totals TYPE STANDARD TABLE OF {{p}}blends. + + METHODS run + IMPORTING it_orders TYPE ty_orders + EXPORTING et_totals TYPE ty_totals + ev_grand_total TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS sums_per_blend FOR TESTING. + METHODS sorted_by_total FOR TESTING. + METHODS tie_break_by_blend FOR TESTING. + METHODS ignores_zero FOR TESTING. + METHODS ignores_negative FOR TESTING. + METHODS empty_input FOR TESTING. + METHODS no_production_line FOR TESTING. + METHODS grand_total FOR TESTING. + METHODS single_line FOR TESTING. + METHODS one_blend_many_lines FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_hidden IMPLEMENTATION. + + METHOD run. + CLEAR: et_totals, ev_grand_total. + CALL FUNCTION '{{P}}ROAST_SUM' + EXPORTING + it_orders = it_orders + IMPORTING + et_totals = et_totals + ev_grand_total = ev_grand_total + EXCEPTIONS + OTHERS = 1. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD sums_per_blend. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000003' blend = 'WEIZ' quantity_kg = '7.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = CONV decfloat34( ls_tot-total_kg ) ). + + READ TABLE lt_totals INTO ls_tot INDEX 2. + cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = CONV decfloat34( ls_tot-total_kg ) ). + ENDMETHOD. + + METHOD sorted_by_total. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'ARAB' quantity_kg = '5.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'ROBU' quantity_kg = '30.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000003' blend = 'KONA' quantity_kg = '12.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ). + READ TABLE lt_totals INTO ls_tot INDEX 2. + cl_abap_unit_assert=>assert_equals( exp = 'KONA' act = ls_tot-blend ). + READ TABLE lt_totals INTO ls_tot INDEX 3. + cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ). + ENDMETHOD. + + METHOD tie_break_by_blend. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'ROBU' quantity_kg = '6.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'ARAB' quantity_kg = '6.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ). + READ TABLE lt_totals INTO ls_tot INDEX 2. + cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ). + ENDMETHOD. + + METHOD ignores_zero. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '4.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = CONV decfloat34( ls_tot-total_kg ) ). + ENDMETHOD. + + METHOD ignores_negative. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '-3.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '4.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_orders TYPE ty_orders. + DATA lt_totals TYPE ty_totals. + DATA lv_grand TYPE decfloat34 VALUE 99. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ). + ENDMETHOD. + + METHOD no_production_line. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '-8.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + DATA lv_grand TYPE decfloat34. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ). + ENDMETHOD. + + METHOD grand_total. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '2.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000003' blend = 'KOLS' quantity_kg = '8.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000004' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + DATA lv_grand TYPE decfloat34. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_grand ). + ENDMETHOD. + + METHOD single_line. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'KONA' quantity_kg = '3.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'KONA' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = CONV decfloat34( ls_tot-total_kg ) ). + ENDMETHOD. + + METHOD one_blend_many_lines. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '1.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '2.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000003' blend = 'PILS' quantity_kg = '3.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000004' blend = 'PILS' quantity_kg = '4.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = CONV decfloat34( ls_tot-total_kg ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0121/reference/roast_sum.func.abap b/tasks_gen/eval/G0121/reference/roast_sum.func.abap new file mode 100644 index 0000000..8ba4d87 --- /dev/null +++ b/tasks_gen/eval/G0121/reference/roast_sum.func.abap @@ -0,0 +1,45 @@ +FUNCTION {{p}}roast_sum + IMPORTING + it_orders TYPE STANDARD TABLE OF {{p}}roasts + EXPORTING + et_totals TYPE STANDARD TABLE OF {{p}}blends + ev_grand_total TYPE decfloat34. + + TYPES: BEGIN OF ty_sum, + blend TYPE {{p}}blends-blend, + total_kg TYPE {{p}}blends-total_kg, + END OF ty_sum. + + DATA lt_sum TYPE HASHED TABLE OF ty_sum WITH UNIQUE KEY blend. + DATA ls_ord TYPE {{p}}roasts. + DATA ls_sum TYPE ty_sum. + DATA ls_tot TYPE {{p}}blends. + + CLEAR: et_totals, ev_grand_total. + + LOOP AT it_orders INTO ls_ord. + IF ls_ord-quantity_kg <= 0. + CONTINUE. + ENDIF. + + READ TABLE lt_sum INTO ls_sum WITH TABLE KEY blend = ls_ord-blend. + IF sy-subrc = 0. + ls_sum-total_kg = ls_sum-total_kg + ls_ord-quantity_kg. + MODIFY TABLE lt_sum FROM ls_sum. + ELSE. + ls_sum-blend = ls_ord-blend. + ls_sum-total_kg = ls_ord-quantity_kg. + INSERT ls_sum INTO TABLE lt_sum. + ENDIF. + ENDLOOP. + + LOOP AT lt_sum INTO ls_sum. + ls_tot-blend = ls_sum-blend. + ls_tot-total_kg = ls_sum-total_kg. + ev_grand_total = ev_grand_total + ls_sum-total_kg. + APPEND ls_tot TO et_totals. + ENDLOOP. + + SORT et_totals BY total_kg DESCENDING blend ASCENDING. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0121/reference/t14_test.clas.abap b/tasks_gen/eval/G0121/reference/t14_test.clas.abap new file mode 100644 index 0000000..9245ec7 --- /dev/null +++ b/tasks_gen/eval/G0121/reference/t14_test.clas.abap @@ -0,0 +1,109 @@ +CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_orders TYPE STANDARD TABLE OF {{p}}roasts. + TYPES ty_totals TYPE STANDARD TABLE OF {{p}}blends. + + METHODS run + IMPORTING it_orders TYPE ty_orders + EXPORTING et_totals TYPE ty_totals + ev_grand_total TYPE decfloat34 + ev_subrc TYPE sysubrc. + + METHODS sums_per_blend FOR TESTING. + METHODS ignores_non_positive FOR TESTING. + METHODS sorted_by_total FOR TESTING. + METHODS tie_break FOR TESTING. + METHODS empty_input FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t14_test IMPLEMENTATION. + + METHOD run. + CLEAR: et_totals, ev_grand_total. + CALL FUNCTION '{{P}}ROAST_SUM' + EXPORTING + it_orders = it_orders + IMPORTING + et_totals = et_totals + ev_grand_total = ev_grand_total + EXCEPTIONS + OTHERS = 1. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD sums_per_blend. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ). + + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = CONV decfloat34( ls_tot-total_kg ) ). + ENDMETHOD. + + METHOD ignores_non_positive. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '-4.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ). + ENDMETHOD. + + METHOD sorted_by_total. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'ARAB' quantity_kg = '5.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'ROBU' quantity_kg = '30.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ). + ENDMETHOD. + + METHOD tie_break. + DATA lt_orders TYPE ty_orders. + APPEND VALUE #( order_id = '0000000001' blend = 'ROBU' quantity_kg = '6.000' ) TO lt_orders. + APPEND VALUE #( order_id = '0000000002' blend = 'ARAB' quantity_kg = '6.000' ) TO lt_orders. + + DATA lt_totals TYPE ty_totals. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + DATA ls_tot TYPE {{p}}blends. + READ TABLE lt_totals INTO ls_tot INDEX 1. + cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ). + ENDMETHOD. + + METHOD empty_input. + DATA lt_orders TYPE ty_orders. + DATA lt_totals TYPE ty_totals. + DATA lv_grand TYPE decfloat34 VALUE 99. + run( EXPORTING it_orders = lt_orders + IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ). + + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ). + cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0121/seed/blends.tabl.abap b/tasks_gen/eval/G0121/seed/blends.tabl.abap new file mode 100644 index 0000000..21b8f58 --- /dev/null +++ b/tasks_gen/eval/G0121/seed/blends.tabl.abap @@ -0,0 +1,7 @@ +@EndUserText.label : 'Roast total per blend' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +define table {{p}}blends { + key client : abap.clnt not null; + key blend : abap.char(4) not null; + total_kg : abap.dec(15,3); +} diff --git a/tasks_gen/eval/G0121/seed/roast_seed.clas.abap b/tasks_gen/eval/G0121/seed/roast_seed.clas.abap new file mode 100644 index 0000000..ff0ed7b --- /dev/null +++ b/tasks_gen/eval/G0121/seed/roast_seed.clas.abap @@ -0,0 +1,28 @@ +CLASS {{p}}roast_seed DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}roast_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_lines TYPE STANDARD TABLE OF {{p}}roasts. + lt_lines = VALUE #( + ( client = sy-mandt order_id = '0000000001' blend = 'PILS' quantity_kg = '120.500' ) + ( client = sy-mandt order_id = '0000000002' blend = 'WEIZ' quantity_kg = '80.000' ) + ( client = sy-mandt order_id = '0000000003' blend = 'PILS' quantity_kg = '60.250' ) + ( client = sy-mandt order_id = '0000000004' blend = 'KOLS' quantity_kg = '45.000' ) + ( client = sy-mandt order_id = '0000000005' blend = 'PILS' quantity_kg = '0.000' ) + ( client = sy-mandt order_id = '0000000006' blend = 'WEIZ' quantity_kg = '20.000' ) + ). + + DELETE FROM {{p}}roasts. + INSERT {{p}}roasts FROM TABLE @lt_lines. + + out->write( |{ sy-dbcnt } roast lines inserted| ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0121/seed/roasts.tabl.abap b/tasks_gen/eval/G0121/seed/roasts.tabl.abap new file mode 100644 index 0000000..10b1205 --- /dev/null +++ b/tasks_gen/eval/G0121/seed/roasts.tabl.abap @@ -0,0 +1,8 @@ +@EndUserText.label : 'Roast order line' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +define table {{p}}roasts { + key client : abap.clnt not null; + key order_id : abap.numc(10) not null; + blend : abap.char(4); + quantity_kg : abap.dec(15,3); +} diff --git a/tasks_gen/eval/G0121/spec.md b/tasks_gen/eval/G0121/spec.md new file mode 100644 index 0000000..21b2879 --- /dev/null +++ b/tasks_gen/eval/G0121/spec.md @@ -0,0 +1,52 @@ +# 1. Goal +A coffee roastery plans its daily production. A planning program calls one +function module for each production day. The function module groups the roast +order lines of the day by coffee blend and returns the total green coffee +quantity of each blend. The planning program shows the blend with the largest +quantity first. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_ROAST exists in package $TMP. +- Table {{P}}ROASTS exists in package $TMP. It stores the roast order lines of + a production day. +- Table {{P}}BLENDS exists in package $TMP. It is the line type of the result + table. +- The class {{P}}ROAST_SEED fills {{P}}ROASTS with sample data. Run it to get + test data. + +# 4. Contract +- Create the function module {{P}}ROAST_SUM in function group {{P}}FG_ROAST. +- Interface of {{P}}ROAST_SUM: + - IMPORTING it_orders TYPE STANDARD TABLE OF {{P}}ROASTS + - EXPORTING et_totals TYPE STANDARD TABLE OF {{P}}BLENDS + - EXPORTING ev_grand_total TYPE decfloat34 +- The function module has no exceptions. + +# 5. Business rules +1. A line of it_orders is a production line if its quantity is greater than 0. + A line with a quantity of 0 or less is not a production line. +2. et_totals has exactly one line for each blend that has at least one + production line in it_orders. +3. The total quantity of a blend is the sum of the quantities of all its + production lines. +4. Sort et_totals by total quantity. The largest total quantity comes first. +5. If two blends have the same total quantity, the blend that is smaller in the + character order comes first. +6. ev_grand_total is the sum of the total quantities of all lines of et_totals. +7. If it_orders is empty, or if it_orders has no production line, et_totals is + empty and ev_grand_total is 0. + +# 6. Constraints +- Release target: 7.58. +- Package: $TMP. Do not use transports. +- Coding standard: Clean ABAP. Keep the function module short. Choose the + internal table kind and the access path that fit the task. +- Out of scope: do not change {{P}}ROASTS, {{P}}BLENDS, {{P}}ROAST_SEED. + +# 7. Acceptance +- The function module {{P}}ROAST_SUM 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/eval/G0121/task.json b/tasks_gen/eval/G0121/task.json new file mode 100644 index 0000000..dd9c9a8 --- /dev/null +++ b/tasks_gen/eval/G0121/task.json @@ -0,0 +1,92 @@ +{ + "id": "G0121", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_ROAST", + "description": "Roast production" + }, + { + "type": "TABL", + "name": "{{P}}ROASTS", + "file": "seed/roasts.tabl.abap", + "description": "Roast order lines of a production day" + }, + { + "type": "TABL", + "name": "{{P}}BLENDS", + "file": "seed/blends.tabl.abap", + "description": "Line type of the result table" + }, + { + "type": "CLAS", + "name": "{{P}}ROAST_SEED", + "file": "seed/roast_seed.clas.abap", + "description": "Sample data for {{P}}ROASTS", + "run": true + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}ROAST_SUM", + "functionGroup": "{{P}}FG_ROAST", + "params": [ + { + "name": "it_orders", + "type": "STANDARD TABLE OF {{P}}ROASTS" + }, + { + "name": "et_totals", + "type": "STANDARD TABLE OF {{P}}BLENDS" + }, + { + "name": "ev_grand_total", + "type": "decfloat34" + } + ] + } + ], + "out_of_scope": [ + "{{P}}ROASTS", + "{{P}}BLENDS", + "{{P}}ROAST_SEED" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T14_HIDDEN", + "file": "hidden/t14_hidden.clas.abap", + "description": "T14 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}ROAST_SUM", + "functionGroup": "{{P}}FG_ROAST", + "file": "reference/roast_sum.func.abap", + "description": "Roast sum module" + }, + { + "type": "CLAS", + "name": "{{P}}T14_TEST", + "file": "reference/t14_test.clas.abap", + "description": "T14 own tests" + } + ], + "craft_checks": [ + "method_length", + "table_kind", + "access_path" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0122/generation.json b/tasks_gen/eval/G0122/generation.json new file mode 100644 index 0000000..ee444fa --- /dev/null +++ b/tasks_gen/eval/G0122/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0122", + "pool": "eval", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "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/eval/G0122/hidden/t31_hidden.clas.abap b/tasks_gen/eval/G0122/hidden/t31_hidden.clas.abap new file mode 100644 index 0000000..c21e07f --- /dev/null +++ b/tasks_gen/eval/G0122/hidden/t31_hidden.clas.abap @@ -0,0 +1,302 @@ +CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_stations TYPE STANDARD TABLE OF {{p}}station. + TYPES ty_moves TYPE STANDARD TABLE OF {{p}}move. + TYPES ty_id TYPE {{p}}station-station_id. + TYPES ty_name TYPE {{p}}station-station_name. + + METHODS given + IMPORTING it_stations TYPE ty_stations + it_moves TYPE ty_moves. + + METHODS rebalance + IMPORTING iv_dry_run TYPE abap_bool DEFAULT abap_false + EXPORTING ev_applied TYPE i + ev_rejected TYPE i. + + METHODS bikes_of + IMPORTING iv_id TYPE ty_id + RETURNING VALUE(rv_bikes) TYPE i. + + METHODS name_of + IMPORTING iv_id TYPE ty_id + RETURNING VALUE(rv_name) TYPE ty_name. + + METHODS applies_one_move FOR TESTING. + METHODS applies_in_move_order FOR TESTING. + METHODS rejects_unknown_station FOR TESTING. + METHODS rejects_insufficient_bikes FOR TESTING. + METHODS rejects_full_target FOR TESTING. + METHODS rejects_non_positive_qty FOR TESTING. + METHODS rejects_same_station FOR TESTING. + METHODS counts_rejections FOR TESTING. + METHODS keeps_untouched_stations FOR TESTING. + METHODS dry_run_keeps_state FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_hidden IMPLEMENTATION. + + METHOD given. + DATA(lt_stations) = it_stations. + DATA(lt_moves) = it_moves. + + LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + DELETE FROM {{p}}station. + DELETE FROM {{p}}move. + INSERT {{p}}station FROM TABLE @lt_stations. + INSERT {{p}}move FROM TABLE @lt_moves. + ENDMETHOD. + + METHOD rebalance. + CALL FUNCTION '{{P}}BIKE_REBALANCE' + EXPORTING + iv_dry_run = iv_dry_run + IMPORTING + ev_applied = ev_applied + ev_rejected = ev_rejected + EXCEPTIONS + OTHERS = 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD bikes_of. + SELECT SINGLE bikes FROM {{p}}station + INTO @rv_bikes + WHERE station_id = @iv_id. + ENDMETHOD. + + METHOD name_of. + SELECT SINGLE station_name FROM {{p}}station + INTO @rv_name + WHERE station_id = @iv_id. + ENDMETHOD. + + METHOD applies_one_move. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 6 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 1 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ). + ENDMETHOD. + + METHOD applies_in_move_order. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) + ( station_id = 'S003' station_name = 'Gamma' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 2 source_id = 'S001' target_id = 'S002' quantity = 5 ) + ( move_no = 1 source_id = 'S002' target_id = 'S003' quantity = 5 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S003' ) exp = 0 ). + ENDMETHOD. + + METHOD rejects_unknown_station. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 6 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S009' target_id = 'S001' quantity = 2 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 6 ). + ENDMETHOD. + + METHOD rejects_insufficient_bikes. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 2 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 5 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ). + ENDMETHOD. + + METHOD rejects_full_target. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 4 bikes = 4 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 1 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 4 ). + ENDMETHOD. + + METHOD rejects_non_positive_qty. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 0 ) + ( move_no = 2 source_id = 'S001' target_id = 'S002' quantity = -3 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ). + ENDMETHOD. + + METHOD rejects_same_station. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S001' quantity = 2 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ). + ENDMETHOD. + + METHOD counts_rejections. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 2 ) + ( move_no = 2 source_id = 'S001' target_id = 'S999' quantity = 1 ) + ( move_no = 3 source_id = 'S001' target_id = 'S002' quantity = 0 ) + ( move_no = 4 source_id = 'S001' target_id = 'S002' quantity = 100 ) + ( move_no = 5 source_id = 'S002' target_id = 'S001' quantity = 1 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 3 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 4 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 1 ). + ENDMETHOD. + + METHOD keeps_untouched_stations. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 4 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 4 ) + ( station_id = 'S003' station_name = 'Gamma' capacity = 12 bikes = 7 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 1 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 3 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S003' ) exp = 7 ). + cl_abap_unit_assert=>assert_equals( act = name_of( 'S003' ) exp = CONV ty_name( 'Gamma' ) ). + ENDMETHOD. + + METHOD dry_run_keeps_state. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 3 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( EXPORTING iv_dry_run = abap_true + IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0122/reference/bike_rebalance.func.abap b/tasks_gen/eval/G0122/reference/bike_rebalance.func.abap new file mode 100644 index 0000000..90a649b --- /dev/null +++ b/tasks_gen/eval/G0122/reference/bike_rebalance.func.abap @@ -0,0 +1,65 @@ +FUNCTION {{p}}bike_rebalance + IMPORTING + iv_dry_run TYPE abap_bool + EXPORTING + ev_applied TYPE i + ev_rejected TYPE i. + + DATA lt_state TYPE STANDARD TABLE OF {{p}}station. + DATA lt_moves TYPE STANDARD TABLE OF {{p}}move. + + SELECT station_id, station_name, capacity, bikes + FROM {{p}}station + INTO TABLE @lt_state. + + SELECT move_no, source_id, target_id, quantity + FROM {{p}}move + INTO TABLE @lt_moves. + + SORT lt_state BY station_id. + SORT lt_moves BY move_no. + + ev_applied = 0. + ev_rejected = 0. + + LOOP AT lt_moves INTO DATA(ls_move). + IF ls_move-quantity <= 0 + OR ls_move-source_id = ls_move-target_id. + ev_rejected = ev_rejected + 1. + CONTINUE. + ENDIF. + + READ TABLE lt_state ASSIGNING FIELD-SYMBOL() + WITH KEY station_id = ls_move-source_id BINARY SEARCH. + IF sy-subrc <> 0. + ev_rejected = ev_rejected + 1. + CONTINUE. + ENDIF. + + READ TABLE lt_state ASSIGNING FIELD-SYMBOL() + WITH KEY station_id = ls_move-target_id BINARY SEARCH. + IF sy-subrc <> 0. + ev_rejected = ev_rejected + 1. + CONTINUE. + ENDIF. + + IF -bikes < ls_move-quantity + OR -capacity - -bikes < ls_move-quantity. + ev_rejected = ev_rejected + 1. + CONTINUE. + ENDIF. + + -bikes = -bikes - ls_move-quantity. + -bikes = -bikes + ls_move-quantity. + ev_applied = ev_applied + 1. + ENDLOOP. + + IF iv_dry_run = abap_false. + LOOP AT lt_state INTO DATA(ls_state). + UPDATE {{p}}station + SET bikes = @ls_state-bikes + WHERE station_id = @ls_state-station_id. + ENDLOOP. + ENDIF. + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0122/reference/t31_test.clas.abap b/tasks_gen/eval/G0122/reference/t31_test.clas.abap new file mode 100644 index 0000000..df2d106 --- /dev/null +++ b/tasks_gen/eval/G0122/reference/t31_test.clas.abap @@ -0,0 +1,130 @@ +CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + + PRIVATE SECTION. + TYPES ty_stations TYPE STANDARD TABLE OF {{p}}station. + TYPES ty_moves TYPE STANDARD TABLE OF {{p}}move. + TYPES ty_id TYPE {{p}}station-station_id. + + METHODS given + IMPORTING it_stations TYPE ty_stations + it_moves TYPE ty_moves. + + METHODS rebalance + IMPORTING iv_dry_run TYPE abap_bool DEFAULT abap_false + EXPORTING ev_applied TYPE i + ev_rejected TYPE i. + + METHODS bikes_of + IMPORTING iv_id TYPE ty_id + RETURNING VALUE(rv_bikes) TYPE i. + + METHODS applies_move FOR TESTING. + METHODS rejects_move FOR TESTING. + METHODS dry_run FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t31_test IMPLEMENTATION. + + METHOD given. + DATA(lt_stations) = it_stations. + DATA(lt_moves) = it_moves. + + LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + DELETE FROM {{p}}station. + DELETE FROM {{p}}move. + INSERT {{p}}station FROM TABLE @lt_stations. + INSERT {{p}}move FROM TABLE @lt_moves. + ENDMETHOD. + + METHOD rebalance. + CALL FUNCTION '{{P}}BIKE_REBALANCE' + EXPORTING + iv_dry_run = iv_dry_run + IMPORTING + ev_applied = ev_applied + ev_rejected = ev_rejected + EXCEPTIONS + OTHERS = 1. + cl_abap_unit_assert=>assert_subrc( exp = 0 ). + ENDMETHOD. + + METHOD bikes_of. + SELECT SINGLE bikes FROM {{p}}station + INTO @rv_bikes + WHERE station_id = @iv_id. + ENDMETHOD. + + METHOD applies_move. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 3 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 3 ). + ENDMETHOD. + + METHOD rejects_move. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 1 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ). + ENDMETHOD. + + METHOD dry_run. + DATA lt_stations TYPE ty_stations. + DATA lt_moves TYPE ty_moves. + DATA lv_applied TYPE i. + DATA lv_rejected TYPE i. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) + ( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ). + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 2 ) ). + + given( it_stations = lt_stations it_moves = lt_moves ). + rebalance( EXPORTING iv_dry_run = abap_true + IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ). + + cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ). + cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ). + cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ). + ENDMETHOD. + +ENDCLASS. diff --git a/tasks_gen/eval/G0122/seed/move.tabl.abap b/tasks_gen/eval/G0122/seed/move.tabl.abap new file mode 100644 index 0000000..b20a9c5 --- /dev/null +++ b/tasks_gen/eval/G0122/seed/move.tabl.abap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Planned bike move' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}move { + key client : abap.clnt not null; + key move_no : abap.int4 not null; + source_id : abap.char(6) not null; + target_id : abap.char(6) not null; + quantity : abap.int4 not null; +} diff --git a/tasks_gen/eval/G0122/seed/seed_bike.clas.abap b/tasks_gen/eval/G0122/seed/seed_bike.clas.abap new file mode 100644 index 0000000..038f52d --- /dev/null +++ b/tasks_gen/eval/G0122/seed/seed_bike.clas.abap @@ -0,0 +1,39 @@ +CLASS {{p}}seed_bike DEFINITION + PUBLIC FINAL CREATE PUBLIC. + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + + +CLASS {{p}}seed_bike IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DATA lt_stations TYPE STANDARD TABLE OF {{p}}station. + DATA lt_moves TYPE STANDARD TABLE OF {{p}}move. + + lt_stations = VALUE #( + ( station_id = 'S001' station_name = 'Central Square' capacity = 20 bikes = 12 ) + ( station_id = 'S002' station_name = 'Harbour' capacity = 15 bikes = 3 ) + ( station_id = 'S003' station_name = 'Main Station' capacity = 10 bikes = 10 ) ). + + lt_moves = VALUE #( + ( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 ) + ( move_no = 2 source_id = 'S002' target_id = 'S003' quantity = 2 ) ). + + LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(). + -client = sy-mandt. + ENDLOOP. + + DELETE FROM {{p}}station. + INSERT {{p}}station FROM TABLE @lt_stations. + + DELETE FROM {{p}}move. + INSERT {{p}}move FROM TABLE @lt_moves. + + out->write( 'Bike sharing sample data inserted.' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0122/seed/station.tabl.abap b/tasks_gen/eval/G0122/seed/station.tabl.abap new file mode 100644 index 0000000..4e32d46 --- /dev/null +++ b/tasks_gen/eval/G0122/seed/station.tabl.abap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Bike station' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}station { + key client : abap.clnt not null; + key station_id : abap.char(6) not null; + station_name : abap.char(30) not null; + capacity : abap.int4 not null; + bikes : abap.int4 not null; +} diff --git a/tasks_gen/eval/G0122/spec.md b/tasks_gen/eval/G0122/spec.md new file mode 100644 index 0000000..09035c8 --- /dev/null +++ b/tasks_gen/eval/G0122/spec.md @@ -0,0 +1,85 @@ +# 1. Goal + +A bike-sharing operator moves bikes between its stations. A background job +calls one function module to apply the planned moves of the operator. The +function module reads the planned moves and the current state of the stations +from the database, applies the moves, and writes the new state of the stations +back to the database. The function module returns the number of applied moves +and the number of rejected moves. + +# 2. Open questions + +None. + +# 3. Context + +- Function group {{P}}FG_BIKE exists in package $TMP. +- Table {{P}}STATION exists in package $TMP. It holds the current state of the + stations of the operator. The table is client-dependent. +- Table {{P}}MOVE exists in package $TMP. It holds the planned moves. The table + is client-dependent. +- The station IDs in {{P}}STATION are unique. The move numbers in {{P}}MOVE are + unique. +- The function module reads and changes the two tables. It works on the data of + the current client. + +# 4. Contract + +Function module {{P}}BIKE_REBALANCE in the function group {{P}}FG_BIKE: + +IMPORTING + iv_dry_run TYPE abap_bool +EXPORTING + ev_applied TYPE i + ev_rejected TYPE i + +Table {{P}}STATION: + + client (key) + station_id (key) TYPE c LENGTH 6 + station_name TYPE c LENGTH 30 + capacity TYPE i + bikes TYPE i + +Table {{P}}MOVE: + + client (key) + move_no (key) TYPE i + source_id TYPE c LENGTH 6 + target_id TYPE c LENGTH 6 + quantity TYPE i + +# 5. Business rules + +1. Read all rows of {{P}}STATION and all rows of {{P}}MOVE. +2. Apply the moves in ascending order of move_no. Do not apply them in the + order of the table. +3. A move is rejected if at least one of these conditions is true: + - quantity is less than or equal to 0; + - source_id and target_id are equal; + - source_id is not the ID of a station in {{P}}STATION; + - target_id is not the ID of a station in {{P}}STATION; + - the source station has fewer bikes than quantity; + - the target station has fewer free docks than quantity. The free docks of + a station are capacity minus bikes. +4. A rejected move does not change any station. +5. An accepted move subtracts quantity from the bikes of the source station + and adds quantity to the bikes of the target station. +6. After the last move, the function module writes the new bikes value of the + stations back to {{P}}STATION. station_name and capacity do not change. +7. If iv_dry_run is true, the function module does not change {{P}}STATION. +8. ev_applied is the number of accepted moves. ev_rejected is the number of + rejected moves. Both numbers are also returned if iv_dry_run is true. + +# 6. Constraints + +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Package: $TMP. Do not create a transport request. +- Clean ABAP. Keep the function module and every method below 40 statements. +- Out of scope: do not change {{P}}STATION, {{P}}MOVE and {{P}}SEED_BIKE. + +# 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/eval/G0122/task.json b/tasks_gen/eval/G0122/task.json new file mode 100644 index 0000000..11f772b --- /dev/null +++ b/tasks_gen/eval/G0122/task.json @@ -0,0 +1,90 @@ +{ + "id": "G0122", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}STATION", + "file": "seed/station.tabl.abap", + "description": "Bike stations of the operator" + }, + { + "type": "TABL", + "name": "{{P}}MOVE", + "file": "seed/move.tabl.abap", + "description": "Planned bike moves" + }, + { + "type": "FUGR", + "name": "{{P}}FG_BIKE", + "description": "Bike sharing function group" + }, + { + "type": "CLAS", + "name": "{{P}}SEED_BIKE", + "file": "seed/seed_bike.clas.abap", + "description": "Sample data for the two tables", + "run": true + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}BIKE_REBALANCE", + "functionGroup": "{{P}}FG_BIKE", + "params": [ + { + "name": "iv_dry_run", + "type": "abap_bool" + }, + { + "name": "ev_applied", + "type": "i" + }, + { + "name": "ev_rejected", + "type": "i" + } + ] + } + ], + "out_of_scope": [ + "{{P}}STATION", + "{{P}}MOVE", + "{{P}}SEED_BIKE" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T31_HIDDEN", + "file": "hidden/t31_hidden.clas.abap", + "description": "Hidden tests for the bike rebalancing" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}BIKE_REBALANCE", + "functionGroup": "{{P}}FG_BIKE", + "file": "reference/bike_rebalance.func.abap", + "description": "Reference implementation of the rebalancing" + }, + { + "type": "CLAS", + "name": "{{P}}T31_TEST", + "file": "reference/t31_test.clas.abap", + "description": "Own ABAP Unit tests for the function module" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0123/faulty/m0_lift_stats.func.abap b/tasks_gen/eval/G0123/faulty/m0_lift_stats.func.abap new file mode 100644 index 0000000..adf17d6 --- /dev/null +++ b/tasks_gen/eval/G0123/faulty/m0_lift_stats.func.abap @@ -0,0 +1,91 @@ +FUNCTION {{p}}lift_stats + IMPORTING + iv_scans TYPE string + iv_lifts TYPE string + EXPORTING + ev_rides TYPE i + ev_passes TYPE i + ev_report TYPE string + EXCEPTIONS + no_lifts. + + TYPES: BEGIN OF ty_lift, + lift TYPE string, + area TYPE string, + END OF ty_lift, + BEGIN OF ty_scan, + pass TYPE string, + lift TYPE string, + END OF ty_scan, + BEGIN OF ty_pass_area, + pass TYPE string, + area TYPE string, + END OF ty_pass_area, + BEGIN OF ty_pass, + pass TYPE string, + rides TYPE i, + areas TYPE i, + END OF ty_pass, + ty_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + DATA lt_lift_lines TYPE ty_lines. + DATA lt_scan_lines TYPE ty_lines. + DATA lt_lifts TYPE HASHED TABLE OF ty_lift WITH UNIQUE KEY lift. + DATA lt_scans TYPE SORTED TABLE OF ty_scan WITH UNIQUE KEY pass lift. + DATA lt_pass_areas TYPE SORTED TABLE OF ty_pass_area WITH UNIQUE KEY pass area. + DATA lt_passes TYPE SORTED TABLE OF ty_pass WITH UNIQUE KEY pass. + DATA ls_lift TYPE ty_lift. + DATA ls_scan TYPE ty_scan. + DATA ls_pass_area TYPE ty_pass_area. + DATA lv_line TYPE string. + DATA lv_key TYPE string. + DATA lv_value TYPE string. + DATA lv_sep TYPE string. + + SPLIT iv_lifts AT cl_abap_char_utilities=>newline INTO TABLE lt_lift_lines. + LOOP AT lt_lift_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL AND lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + INSERT VALUE #( lift = lv_key area = lv_value ) INTO TABLE lt_lifts. + ENDLOOP. + + IF lines( lt_lifts ) = 0. + RAISE no_lifts. + ENDIF. + + SPLIT iv_scans AT cl_abap_char_utilities=>newline INTO TABLE lt_scan_lines. + LOOP AT lt_scan_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + IF NOT line_exists( lt_lifts[ lift = lv_value ] ). + CONTINUE. + ENDIF. + INSERT VALUE #( pass = lv_key lift = lv_value ) INTO TABLE lt_scans. + ENDLOOP. + + LOOP AT lt_scans INTO ls_scan. + READ TABLE lt_lifts INTO ls_lift WITH TABLE KEY lift = ls_scan-lift. + INSERT VALUE #( pass = ls_scan-pass ) INTO TABLE lt_passes. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_scan-pass. + -rides = -rides + 1. + INSERT VALUE #( pass = ls_scan-pass area = ls_lift-area ) INTO TABLE lt_pass_areas. + ENDLOOP. + + LOOP AT lt_pass_areas INTO ls_pass_area. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_pass_area-pass. + -areas = -areas + 1. + ENDLOOP. + + LOOP AT lt_passes ASSIGNING FIELD-SYMBOL(). + ev_report = ev_report && lv_sep && |{ -pass };{ -rides };{ -areas }|. + lv_sep = cl_abap_char_utilities=>newline. + ENDLOOP. + + ev_rides = lines( lt_scans ). + ev_passes = lines( lt_passes ). + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0123/faulty/m1_lift_stats.func.abap b/tasks_gen/eval/G0123/faulty/m1_lift_stats.func.abap new file mode 100644 index 0000000..464efa8 --- /dev/null +++ b/tasks_gen/eval/G0123/faulty/m1_lift_stats.func.abap @@ -0,0 +1,91 @@ +FUNCTION {{p}}lift_stats + IMPORTING + iv_scans TYPE string + iv_lifts TYPE string + EXPORTING + ev_rides TYPE i + ev_passes TYPE i + ev_report TYPE string + EXCEPTIONS + no_lifts. + + TYPES: BEGIN OF ty_lift, + lift TYPE string, + area TYPE string, + END OF ty_lift, + BEGIN OF ty_scan, + pass TYPE string, + lift TYPE string, + END OF ty_scan, + BEGIN OF ty_pass_area, + pass TYPE string, + area TYPE string, + END OF ty_pass_area, + BEGIN OF ty_pass, + pass TYPE string, + rides TYPE i, + areas TYPE i, + END OF ty_pass, + ty_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + DATA lt_lift_lines TYPE ty_lines. + DATA lt_scan_lines TYPE ty_lines. + DATA lt_lifts TYPE HASHED TABLE OF ty_lift WITH UNIQUE KEY lift. + DATA lt_scans TYPE SORTED TABLE OF ty_scan WITH UNIQUE KEY pass lift. + DATA lt_pass_areas TYPE SORTED TABLE OF ty_pass_area WITH UNIQUE KEY pass area. + DATA lt_passes TYPE SORTED TABLE OF ty_pass WITH UNIQUE KEY pass. + DATA ls_lift TYPE ty_lift. + DATA ls_scan TYPE ty_scan. + DATA ls_pass_area TYPE ty_pass_area. + DATA lv_line TYPE string. + DATA lv_key TYPE string. + DATA lv_value TYPE string. + DATA lv_sep TYPE string. + + SPLIT iv_lifts AT cl_abap_char_utilities=>newline INTO TABLE lt_lift_lines. + LOOP AT lt_lift_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL AND lv_value CA ';'. + CONTINUE. + ENDIF. + INSERT VALUE #( lift = lv_key area = lv_value ) INTO TABLE lt_lifts. + ENDLOOP. + + IF lines( lt_lifts ) = 0. + RAISE no_lifts. + ENDIF. + + SPLIT iv_scans AT cl_abap_char_utilities=>newline INTO TABLE lt_scan_lines. + LOOP AT lt_scan_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + IF NOT line_exists( lt_lifts[ lift = lv_value ] ). + CONTINUE. + ENDIF. + INSERT VALUE #( pass = lv_key lift = lv_value ) INTO TABLE lt_scans. + ENDLOOP. + + LOOP AT lt_scans INTO ls_scan. + READ TABLE lt_lifts INTO ls_lift WITH TABLE KEY lift = ls_scan-lift. + INSERT VALUE #( pass = ls_scan-pass ) INTO TABLE lt_passes. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_scan-pass. + -rides = -rides + 1. + INSERT VALUE #( pass = ls_scan-pass area = ls_lift-area ) INTO TABLE lt_pass_areas. + ENDLOOP. + + LOOP AT lt_pass_areas INTO ls_pass_area. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_pass_area-pass. + -areas = -areas + 1. + ENDLOOP. + + LOOP AT lt_passes ASSIGNING FIELD-SYMBOL(). + ev_report = ev_report && lv_sep && |{ -pass };{ -rides };{ -areas }|. + lv_sep = cl_abap_char_utilities=>newline. + ENDLOOP. + + ev_rides = lines( lt_scans ). + ev_passes = lines( lt_passes ). + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0123/faulty/m2_lift_stats.func.abap b/tasks_gen/eval/G0123/faulty/m2_lift_stats.func.abap new file mode 100644 index 0000000..945d51f --- /dev/null +++ b/tasks_gen/eval/G0123/faulty/m2_lift_stats.func.abap @@ -0,0 +1,91 @@ +FUNCTION {{p}}lift_stats + IMPORTING + iv_scans TYPE string + iv_lifts TYPE string + EXPORTING + ev_rides TYPE i + ev_passes TYPE i + ev_report TYPE string + EXCEPTIONS + no_lifts. + + TYPES: BEGIN OF ty_lift, + lift TYPE string, + area TYPE string, + END OF ty_lift, + BEGIN OF ty_scan, + pass TYPE string, + lift TYPE string, + END OF ty_scan, + BEGIN OF ty_pass_area, + pass TYPE string, + area TYPE string, + END OF ty_pass_area, + BEGIN OF ty_pass, + pass TYPE string, + rides TYPE i, + areas TYPE i, + END OF ty_pass, + ty_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + DATA lt_lift_lines TYPE ty_lines. + DATA lt_scan_lines TYPE ty_lines. + DATA lt_lifts TYPE HASHED TABLE OF ty_lift WITH UNIQUE KEY lift. + DATA lt_scans TYPE SORTED TABLE OF ty_scan WITH UNIQUE KEY pass lift. + DATA lt_pass_areas TYPE SORTED TABLE OF ty_pass_area WITH UNIQUE KEY pass area. + DATA lt_passes TYPE SORTED TABLE OF ty_pass WITH UNIQUE KEY pass. + DATA ls_lift TYPE ty_lift. + DATA ls_scan TYPE ty_scan. + DATA ls_pass_area TYPE ty_pass_area. + DATA lv_line TYPE string. + DATA lv_key TYPE string. + DATA lv_value TYPE string. + DATA lv_sep TYPE string. + + SPLIT iv_lifts AT cl_abap_char_utilities=>newline INTO TABLE lt_lift_lines. + LOOP AT lt_lift_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + INSERT VALUE #( lift = lv_key area = lv_value ) INTO TABLE lt_lifts. + ENDLOOP. + + IF lines( lt_lifts ) = 0. + RAISE no_lifts. + ENDIF. + + SPLIT iv_scans AT cl_abap_char_utilities=>newline INTO TABLE lt_scan_lines. + LOOP AT lt_scan_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL AND lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + IF NOT line_exists( lt_lifts[ lift = lv_value ] ). + CONTINUE. + ENDIF. + INSERT VALUE #( pass = lv_key lift = lv_value ) INTO TABLE lt_scans. + ENDLOOP. + + LOOP AT lt_scans INTO ls_scan. + READ TABLE lt_lifts INTO ls_lift WITH TABLE KEY lift = ls_scan-lift. + INSERT VALUE #( pass = ls_scan-pass ) INTO TABLE lt_passes. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_scan-pass. + -rides = -rides + 1. + INSERT VALUE #( pass = ls_scan-pass area = ls_lift-area ) INTO TABLE lt_pass_areas. + ENDLOOP. + + LOOP AT lt_pass_areas INTO ls_pass_area. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_pass_area-pass. + -areas = -areas + 1. + ENDLOOP. + + LOOP AT lt_passes ASSIGNING FIELD-SYMBOL(). + ev_report = ev_report && lv_sep && |{ -pass };{ -rides };{ -areas }|. + lv_sep = cl_abap_char_utilities=>newline. + ENDLOOP. + + ev_rides = lines( lt_scans ). + ev_passes = lines( lt_passes ). + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0123/faulty/m4_lift_stats.func.abap b/tasks_gen/eval/G0123/faulty/m4_lift_stats.func.abap new file mode 100644 index 0000000..c5eca92 --- /dev/null +++ b/tasks_gen/eval/G0123/faulty/m4_lift_stats.func.abap @@ -0,0 +1,91 @@ +FUNCTION {{p}}lift_stats + IMPORTING + iv_scans TYPE string + iv_lifts TYPE string + EXPORTING + ev_rides TYPE i + ev_passes TYPE i + ev_report TYPE string + EXCEPTIONS + no_lifts. + + TYPES: BEGIN OF ty_lift, + lift TYPE string, + area TYPE string, + END OF ty_lift, + BEGIN OF ty_scan, + pass TYPE string, + lift TYPE string, + END OF ty_scan, + BEGIN OF ty_pass_area, + pass TYPE string, + area TYPE string, + END OF ty_pass_area, + BEGIN OF ty_pass, + pass TYPE string, + rides TYPE i, + areas TYPE i, + END OF ty_pass, + ty_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + DATA lt_lift_lines TYPE ty_lines. + DATA lt_scan_lines TYPE ty_lines. + DATA lt_lifts TYPE HASHED TABLE OF ty_lift WITH UNIQUE KEY lift. + DATA lt_scans TYPE SORTED TABLE OF ty_scan WITH UNIQUE KEY pass lift. + DATA lt_pass_areas TYPE SORTED TABLE OF ty_pass_area WITH UNIQUE KEY pass area. + DATA lt_passes TYPE SORTED TABLE OF ty_pass WITH UNIQUE KEY pass. + DATA ls_lift TYPE ty_lift. + DATA ls_scan TYPE ty_scan. + DATA ls_pass_area TYPE ty_pass_area. + DATA lv_line TYPE string. + DATA lv_key TYPE string. + DATA lv_value TYPE string. + DATA lv_sep TYPE string. + + SPLIT iv_lifts AT cl_abap_char_utilities=>newline INTO TABLE lt_lift_lines. + LOOP AT lt_lift_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + INSERT VALUE #( lift = lv_key area = lv_value ) INTO TABLE lt_lifts. + ENDLOOP. + + IF lines( lt_lifts ) = 0. + RAISE no_lifts. + ENDIF. + + SPLIT iv_scans AT cl_abap_char_utilities=>newline INTO TABLE lt_scan_lines. + LOOP AT lt_scan_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + IF NOT line_exists( lt_lifts[ lift = lv_value ] ). + CONTINUE. + ENDIF. + INSERT VALUE #( pass = lv_key lift = lv_value ) INTO TABLE lt_scans. + ENDLOOP. + + LOOP AT lt_scans INTO ls_scan. + READ TABLE lt_lifts INTO ls_lift WITH TABLE KEY lift = ls_scan-lift. + INSERT VALUE #( pass = ls_scan-pass ) INTO TABLE lt_passes. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_scan-pass. + -rides = -rides - 1. + INSERT VALUE #( pass = ls_scan-pass area = ls_lift-area ) INTO TABLE lt_pass_areas. + ENDLOOP. + + LOOP AT lt_pass_areas INTO ls_pass_area. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_pass_area-pass. + -areas = -areas + 1. + ENDLOOP. + + LOOP AT lt_passes ASSIGNING FIELD-SYMBOL(). + ev_report = ev_report && lv_sep && |{ -pass };{ -rides };{ -areas }|. + lv_sep = cl_abap_char_utilities=>newline. + ENDLOOP. + + ev_rides = lines( lt_scans ). + ev_passes = lines( lt_passes ). + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0123/generation.json b/tasks_gen/eval/G0123/generation.json new file mode 100644 index 0000000..c5e521b --- /dev/null +++ b/tasks_gen/eval/G0123/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0123", + "pool": "eval", + "object_type": "FUNC", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 4, + "killed": 2, + "ok": false + } + }, + { + "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/eval/G0123/hidden/t21_hidden.clas.abap b/tasks_gen/eval/G0123/hidden/t21_hidden.clas.abap new file mode 100644 index 0000000..418b456 --- /dev/null +++ b/tasks_gen/eval/G0123/hidden/t21_hidden.clas.abap @@ -0,0 +1,125 @@ +CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS call IMPORTING iv_scans TYPE string iv_lifts TYPE string + EXPORTING ev_rides TYPE i ev_passes TYPE i ev_report TYPE string + ev_subrc TYPE sysubrc. + METHODS empty_scans FOR TESTING. + METHODS one_scan FOR TESTING. + METHODS duplicate_scan FOR TESTING. + METHODS unknown_lift FOR TESTING. + METHODS invalid_lines FOR TESTING. + METHODS sorted_report FOR TESTING. + METHODS distinct_areas FOR TESTING. + METHODS duplicate_lift FOR TESTING. + METHODS two_passes FOR TESTING. + METHODS missing_lift_list FOR TESTING. + METHODS bad_lift_line FOR TESTING. + METHODS empty_lift_name FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_hidden IMPLEMENTATION. + METHOD call. + CALL FUNCTION '{{P}}LIFT_STATS' + EXPORTING + iv_scans = iv_scans + iv_lifts = iv_lifts + IMPORTING + ev_rides = ev_rides + ev_passes = ev_passes + ev_report = ev_report + EXCEPTIONS + no_lifts = 1 + OTHERS = 2. + ev_subrc = sy-subrc. + ENDMETHOD. + + METHOD empty_scans. + call( EXPORTING iv_scans = `` iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = `` act = lv_report ). + ENDMETHOD. + + METHOD one_scan. + call( EXPORTING iv_scans = `P1;L1` iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = `P1;1;1` act = lv_report ). + ENDMETHOD. + + METHOD duplicate_scan. + call( EXPORTING iv_scans = |P1;L1\nP1;L1| iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = `P1;1;1` act = lv_report ). + ENDMETHOD. + + METHOD unknown_lift. + call( EXPORTING iv_scans = `P1;L9` iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = `` act = lv_report ). + ENDMETHOD. + + METHOD invalid_lines. + call( EXPORTING iv_scans = |P1;L1\nP1L1\n;L1\nP1;\nP1;L1;X\nP2;L1| iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = |P1;1;1\nP2;1;1| act = lv_report ). + ENDMETHOD. + + METHOD sorted_report. + call( EXPORTING iv_scans = |P3;L1\nP1;L1\nP2;L1| iv_lifts = `L1;North` + IMPORTING ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = |P1;1;1\nP2;1;1\nP3;1;1| act = lv_report ). + ENDMETHOD. + + METHOD distinct_areas. + call( EXPORTING iv_scans = |P1;L1\nP1;L2\nP1;L3| iv_lifts = |L1;North\nL2;North\nL3;South| + IMPORTING ev_rides = DATA(lv_rides) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = `P1;3;2` act = lv_report ). + ENDMETHOD. + + METHOD duplicate_lift. + call( EXPORTING iv_scans = |P1;L1\nP1;L2| iv_lifts = |L1;North\nL2;North\nL1;South| + IMPORTING ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = `P1;2;1` act = lv_report ). + ENDMETHOD. + + METHOD two_passes. + call( EXPORTING iv_scans = |P1;L1\nP1;L2\nP2;L2| iv_lifts = |L1;North\nL2;South| + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = |P1;2;2\nP2;1;1| act = lv_report ). + ENDMETHOD. + + METHOD missing_lift_list. + call( EXPORTING iv_scans = `P1;L1` iv_lifts = `` + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = CONV sysubrc( 1 ) act = lv_subrc ). + ENDMETHOD. + + METHOD bad_lift_line. + call( EXPORTING iv_scans = |P1;L1\nP1;L2| iv_lifts = |L1\nL2;South| + IMPORTING ev_rides = DATA(lv_rides) ev_passes = DATA(lv_passes) ev_report = DATA(lv_report) ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = CONV sysubrc( 0 ) act = lv_subrc ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_passes ). + cl_abap_unit_assert=>assert_equals( exp = `P1;1;1` act = lv_report ). + ENDMETHOD. + + METHOD empty_lift_name. + call( EXPORTING iv_scans = `P1;L1` iv_lifts = `;North` + IMPORTING ev_subrc = DATA(lv_subrc) ). + cl_abap_unit_assert=>assert_equals( exp = CONV sysubrc( 1 ) act = lv_subrc ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0123/mutation.json b/tasks_gen/eval/G0123/mutation.json new file mode 100644 index 0000000..4df6bc9 --- /dev/null +++ b/tasks_gen/eval/G0123/mutation.json @@ -0,0 +1,60 @@ +{ + "task": "G0123", + "mutants": [ + { + "object": "{{P}}LIFT_STATS", + "mutant": "line 48: OR -> AND (logic)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "BAD_LIFT_LINE", + "EMPTY_LIFT_NAME" + ] + }, + { + "object": "{{P}}LIFT_STATS", + "mutant": "line 48: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "BAD_LIFT_LINE" + ] + }, + { + "object": "{{P}}LIFT_STATS", + "mutant": "line 61: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "INVALID_LINES" + ] + }, + { + "object": "{{P}}LIFT_STATS", + "mutant": "line 64: = -> <> (eq)", + "status": "invalid", + "hidden": "0/0", + "failed_tests": [] + }, + { + "object": "{{P}}LIFT_STATS", + "mutant": "line 74: + -> - (arith)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "BAD_LIFT_LINE", + "DISTINCT_AREAS", + "DUPLICATE_LIFT", + "DUPLICATE_SCAN", + "INVALID_LINES", + "ONE_SCAN", + "SORTED_REPORT", + "TWO_PASSES" + ] + } + ], + "valid": 4, + "killed": 4, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0123/reference/lift_stats.func.abap b/tasks_gen/eval/G0123/reference/lift_stats.func.abap new file mode 100644 index 0000000..4e13d84 --- /dev/null +++ b/tasks_gen/eval/G0123/reference/lift_stats.func.abap @@ -0,0 +1,91 @@ +FUNCTION {{p}}lift_stats + IMPORTING + iv_scans TYPE string + iv_lifts TYPE string + EXPORTING + ev_rides TYPE i + ev_passes TYPE i + ev_report TYPE string + EXCEPTIONS + no_lifts. + + TYPES: BEGIN OF ty_lift, + lift TYPE string, + area TYPE string, + END OF ty_lift, + BEGIN OF ty_scan, + pass TYPE string, + lift TYPE string, + END OF ty_scan, + BEGIN OF ty_pass_area, + pass TYPE string, + area TYPE string, + END OF ty_pass_area, + BEGIN OF ty_pass, + pass TYPE string, + rides TYPE i, + areas TYPE i, + END OF ty_pass, + ty_lines TYPE STANDARD TABLE OF string WITH EMPTY KEY. + + DATA lt_lift_lines TYPE ty_lines. + DATA lt_scan_lines TYPE ty_lines. + DATA lt_lifts TYPE HASHED TABLE OF ty_lift WITH UNIQUE KEY lift. + DATA lt_scans TYPE SORTED TABLE OF ty_scan WITH UNIQUE KEY pass lift. + DATA lt_pass_areas TYPE SORTED TABLE OF ty_pass_area WITH UNIQUE KEY pass area. + DATA lt_passes TYPE SORTED TABLE OF ty_pass WITH UNIQUE KEY pass. + DATA ls_lift TYPE ty_lift. + DATA ls_scan TYPE ty_scan. + DATA ls_pass_area TYPE ty_pass_area. + DATA lv_line TYPE string. + DATA lv_key TYPE string. + DATA lv_value TYPE string. + DATA lv_sep TYPE string. + + SPLIT iv_lifts AT cl_abap_char_utilities=>newline INTO TABLE lt_lift_lines. + LOOP AT lt_lift_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + INSERT VALUE #( lift = lv_key area = lv_value ) INTO TABLE lt_lifts. + ENDLOOP. + + IF lines( lt_lifts ) = 0. + RAISE no_lifts. + ENDIF. + + SPLIT iv_scans AT cl_abap_char_utilities=>newline INTO TABLE lt_scan_lines. + LOOP AT lt_scan_lines INTO lv_line. + SPLIT lv_line AT ';' INTO lv_key lv_value. + IF lv_key IS INITIAL OR lv_value IS INITIAL OR lv_value CA ';'. + CONTINUE. + ENDIF. + IF NOT line_exists( lt_lifts[ lift = lv_value ] ). + CONTINUE. + ENDIF. + INSERT VALUE #( pass = lv_key lift = lv_value ) INTO TABLE lt_scans. + ENDLOOP. + + LOOP AT lt_scans INTO ls_scan. + READ TABLE lt_lifts INTO ls_lift WITH TABLE KEY lift = ls_scan-lift. + INSERT VALUE #( pass = ls_scan-pass ) INTO TABLE lt_passes. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_scan-pass. + -rides = -rides + 1. + INSERT VALUE #( pass = ls_scan-pass area = ls_lift-area ) INTO TABLE lt_pass_areas. + ENDLOOP. + + LOOP AT lt_pass_areas INTO ls_pass_area. + READ TABLE lt_passes ASSIGNING FIELD-SYMBOL() WITH TABLE KEY pass = ls_pass_area-pass. + -areas = -areas + 1. + ENDLOOP. + + LOOP AT lt_passes ASSIGNING FIELD-SYMBOL(). + ev_report = ev_report && lv_sep && |{ -pass };{ -rides };{ -areas }|. + lv_sep = cl_abap_char_utilities=>newline. + ENDLOOP. + + ev_rides = lines( lt_scans ). + ev_passes = lines( lt_passes ). + +ENDFUNCTION. diff --git a/tasks_gen/eval/G0123/reference/t21_test.clas.abap b/tasks_gen/eval/G0123/reference/t21_test.clas.abap new file mode 100644 index 0000000..d8a0387 --- /dev/null +++ b/tasks_gen/eval/G0123/reference/t21_test.clas.abap @@ -0,0 +1,53 @@ +CLASS {{p}}t21_test DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS stats IMPORTING iv_scans TYPE string iv_lifts TYPE string + EXPORTING ev_rides TYPE i ev_passes TYPE i ev_report TYPE string. + METHODS one_pass FOR TESTING. + METHODS duplicates FOR TESTING. + METHODS unknown_lift FOR TESTING. + METHODS sorted_report FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t21_test IMPLEMENTATION. + METHOD stats. + CALL FUNCTION '{{P}}LIFT_STATS' + EXPORTING + iv_scans = iv_scans + iv_lifts = iv_lifts + IMPORTING + ev_rides = ev_rides + ev_passes = ev_passes + ev_report = ev_report + EXCEPTIONS + no_lifts = 1 + OTHERS = 2. + ENDMETHOD. + + METHOD one_pass. + stats( EXPORTING iv_scans = `P1;L1` iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = `P1;1;1` act = lv_report ). + ENDMETHOD. + + METHOD duplicates. + stats( EXPORTING iv_scans = |P1;L1\nP1;L1| iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rides ). + ENDMETHOD. + + METHOD unknown_lift. + stats( EXPORTING iv_scans = `P1;L9` iv_lifts = `L1;North` + IMPORTING ev_rides = DATA(lv_rides) ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rides ). + cl_abap_unit_assert=>assert_equals( exp = `` act = lv_report ). + ENDMETHOD. + + METHOD sorted_report. + stats( EXPORTING iv_scans = |P2;L1\nP1;L1| iv_lifts = `L1;North` + IMPORTING ev_report = DATA(lv_report) ). + cl_abap_unit_assert=>assert_equals( exp = |P1;1;1\nP2;1;1| act = lv_report ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0123/spec.md b/tasks_gen/eval/G0123/spec.md new file mode 100644 index 0000000..cceb426 --- /dev/null +++ b/tasks_gen/eval/G0123/spec.md @@ -0,0 +1,55 @@ +# 1. Goal +Summarise the lift pass scans of one day at a ski resort. The operations team +needs one function module. The function module reads the scans and the lift +list. It returns the statistics and a report. + +# 2. Open questions +None. + +# 3. Context +- Function group {{P}}FG_LIFT exists in package $TMP. +- The turnstile system sends the scans of the day as a text. Each line of this +text has the format `pass;lift`. +- The resort keeps a list of its lifts. Each line of this list has the format +`lift;area`. +- The lines of both texts are separated by the newline character +(cl_abap_char_utilities=>newline). + +# 4. Contract +- Create the function module {{P}}LIFT_STATS in function group {{P}}FG_LIFT. +- Interface: + - Importing: iv_scans TYPE string, iv_lifts TYPE string + - Exporting: ev_rides TYPE i, ev_passes TYPE i, ev_report TYPE string + - Exceptions: no_lifts + +# 5. Business rules +1. A line of a text is valid if it contains exactly one `;` and the text + before and the text after this `;` are both not empty. Ignore every other + line. +2. If iv_lifts contains no valid line, raise the exception no_lifts. Do not + change the exporting parameters in this case. +3. The lift list gives the area of a lift. If a lift occurs more than once, + the first line wins. +4. Ignore a scan if its lift is not in the lift list. +5. Count a scan once for a pass and a lift. If the same pass and the same lift + occur more than once, they count once. +6. For each pass, count its rides and its areas. The rides are the number of + the distinct lifts of the pass. The areas are the number of the distinct + areas of these lifts. +7. ev_rides is the total number of counted scans. ev_passes is the number of + passes. +8. ev_report has one line for each pass. Sort the lines by pass in ascending + order. A line has the format `pass;rides;areas`. Separate the lines by the + newline character. Do not put a newline character after the last line. If + there is no pass, ev_report is empty. + +# 6. Constraints +- Release target: 8.16. +- Package: $TMP. Do not use transports. +- Coding standard: Clean ABAP. Keep every procedure below 40 statements. +- Out of scope: none. + +# 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/eval/G0123/task.json b/tasks_gen/eval/G0123/task.json new file mode 100644 index 0000000..57889d2 --- /dev/null +++ b/tasks_gen/eval/G0123/task.json @@ -0,0 +1,76 @@ +{ + "id": "G0123", + "category": "C", + "object_type": "FUNC", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "FUGR", + "name": "{{P}}FG_LIFT", + "description": "Function group for the lift statistics" + } + ], + "contract": [ + { + "type": "FUNC", + "name": "{{P}}LIFT_STATS", + "functionGroup": "{{P}}FG_LIFT", + "params": [ + { + "name": "iv_scans", + "type": "string" + }, + { + "name": "iv_lifts", + "type": "string" + }, + { + "name": "ev_rides", + "type": "i" + }, + { + "name": "ev_passes", + "type": "i" + }, + { + "name": "ev_report", + "type": "string" + } + ] + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T21_HIDDEN", + "file": "hidden/t21_hidden.clas.abap", + "description": "T21 hidden tests" + } + ], + "reference": [ + { + "type": "FUNC", + "name": "{{P}}LIFT_STATS", + "functionGroup": "{{P}}FG_LIFT", + "file": "reference/lift_stats.func.abap", + "description": "Lift statistics" + }, + { + "type": "CLAS", + "name": "{{P}}T21_TEST", + "file": "reference/t21_test.clas.abap", + "description": "T21 own tests" + } + ], + "craft_checks": [ + "method_length", + "no_nested_loops" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0124/generation.json b/tasks_gen/eval/G0124/generation.json new file mode 100644 index 0000000..8003896 --- /dev/null +++ b/tasks_gen/eval/G0124/generation.json @@ -0,0 +1,38 @@ +{ + "id": "G0124", + "pool": "eval", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 0, + "killed": 0, + "ok": false + } + }, + { + "stage": "validate", + "oracle": 80.6, + "null": 0 + }, + { + "stage": "bundle", + "errors": [ + "seed: file seed/t20 seed.clas.abap missing" + ], + "by_check": { + "structure": 1 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/eval/G0124/hidden/t20_hidden.clas.abap b/tasks_gen/eval/G0124/hidden/t20_hidden.clas.abap new file mode 100644 index 0000000..741c006 --- /dev/null +++ b/tasks_gen/eval/G0124/hidden/t20_hidden.clas.abap @@ -0,0 +1,212 @@ +CLASS {{p}}t20_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_region TYPE RANGE OF {{p}}farm-region. + METHODS run + IMPORTING iv_from TYPE d + iv_to TYPE d + it_region TYPE tt_region OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_farms_listed FOR TESTING. + METHODS litres_summed FOR TESTING. + METHODS delivery_count FOR TESTING. + METHODS date_bounds FOR TESTING. + METHODS date_range_wide FOR TESTING. + METHODS region_filter FOR TESTING. + METHODS region_exclude FOR TESTING. + METHODS sorted_by_litres FOR TESTING. + METHODS tie_break_farm_id FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t20_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}milk_sum WITH p_from = iv_from WITH p_to = iv_to + WITH s_reg IN it_region AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). +  ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_farms_listed. + cl_abap_unit_assert=>assert_equals( + exp = 5 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ). + cl_abap_unit_assert=>assert_equals( + exp = 2 + act = lines_of( run( iv_from = '20260101' iv_to = '20260131' + it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'CH' ) ) ) ) ). + ENDMETHOD. + + METHOD litres_summed. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.50` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.00` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD delivery_count. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DELIVERIES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DELIVERIES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1003` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `0` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `DELIVERIES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `0.00` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD date_bounds. + DATA(lr_one) = run( iv_from = '20260105' iv_to = '20260105' ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( lr_one ) ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_one iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `120.50` + act = value_of( ir_data = lr_one iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1` + act = value_of( ir_data = lr_one iv_row = 1 iv_col = `DELIVERIES` ) ). + + DATA(lr_two) = run( iv_from = '20260105' iv_to = '20260106' ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( lr_two ) ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_two iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.00` + act = value_of( ir_data = lr_two iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_two iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `120.50` + act = value_of( ir_data = lr_two iv_row = 2 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD date_range_wide. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260228' ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `240.00` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DELIVERIES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.50` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DELIVERIES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1003` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `0.00` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD region_filter. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'CH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.50` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `200.00` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD region_exclude. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' + it_region = VALUE #( ( sign = 'E' option = 'EQ' low = 'CH' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `1002` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `50.25` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1004` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `50.25` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1003` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `0.00` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LITRES` ) ). + ENDMETHOD. + + METHOD sorted_by_litres. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `1000` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1002` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1004` + act = value_of( ir_data = lr_data iv_row = 4 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1003` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `Alpine Dairy` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `CH` + act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ). + cl_abap_unit_assert=>assert_equals( exp = `Hilltop Farm` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_NAME` ) ). + cl_abap_unit_assert=>assert_equals( exp = `AT` + act = value_of( ir_data = lr_data iv_row = 5 iv_col = `REGION` ) ). + ENDMETHOD. + + METHOD tie_break_farm_id. + DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ). + cl_abap_unit_assert=>assert_equals( exp = `50.25` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `50.25` + act = value_of( ir_data = lr_data iv_row = 4 iv_col = `LITRES` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1002` + act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1004` + act = value_of( ir_data = lr_data iv_row = 4 iv_col = `FARM_ID` ) ). + + DATA(lr_zero) = run( iv_from = '20260105' iv_to = '20260105' ). + cl_abap_unit_assert=>assert_equals( exp = `1001` + act = value_of( ir_data = lr_zero iv_row = 2 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1002` + act = value_of( ir_data = lr_zero iv_row = 3 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1003` + act = value_of( ir_data = lr_zero iv_row = 4 iv_col = `FARM_ID` ) ). + cl_abap_unit_assert=>assert_equals( exp = `1004` + act = value_of( ir_data = lr_zero iv_row = 5 iv_col = `FARM_ID` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0124/mutation.json b/tasks_gen/eval/G0124/mutation.json new file mode 100644 index 0000000..a2508c3 --- /dev/null +++ b/tasks_gen/eval/G0124/mutation.json @@ -0,0 +1,96 @@ +{ + "task": "T20", + "mutants": [ + { + "object": "{{P}}MILK_SUM", + "mutant": "BETWEEN -> >= only (rel)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "ALL_FARMS_LISTED", + "LITRES_SUMMED", + "DELIVERY_COUNT", + "DATE_BOUNDS", + "DATE_RANGE_WIDE", + "REGION_FILTER", + "REGION_EXCLUDE", + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + }, + { + "object": "{{P}}MILK_SUM", + "mutant": "BETWEEN -> <= only (rel)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "LITRES_SUMMED", + "DATE_BOUNDS", + "DATE_RANGE_WIDE", + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + }, + { + "object": "{{P}}MILK_SUM", + "mutant": "region IN -> region NOT IN (logic)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "ALL_FARMS_LISTED", + "LITRES_SUMMED", + "DELIVERY_COUNT", + "DATE_BOUNDS", + "DATE_RANGE_WIDE", + "REGION_FILTER", + "REGION_EXCLUDE", + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + }, + { + "object": "{{P}}MILK_SUM", + "mutant": "farm without delivery skipped (inner join)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "ALL_FARMS_LISTED", + "DELIVERY_COUNT", + "DATE_BOUNDS", + "DATE_RANGE_WIDE", + "REGION_EXCLUDE", + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + }, + { + "object": "{{P}}MILK_SUM", + "mutant": "SORT litres DESCENDING -> ASCENDING (rel)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "LITRES_SUMMED", + "DATE_BOUNDS", + "DATE_RANGE_WIDE", + "REGION_FILTER", + "REGION_EXCLUDE", + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + }, + { + "object": "{{P}}MILK_SUM", + "mutant": "secondary sort farm_id removed (tie)", + "status": "killed", + "hidden": "0/9", + "failed_tests": [ + "SORTED_BY_LITRES", + "TIE_BREAK_FARM_ID" + ] + } + ], + "valid": 6, + "killed": 6, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0124/reference/milk_sum.prog.abap b/tasks_gen/eval/G0124/reference/milk_sum.prog.abap new file mode 100644 index 0000000..82a72bc --- /dev/null +++ b/tasks_gen/eval/G0124/reference/milk_sum.prog.abap @@ -0,0 +1,170 @@ +REPORT {{p}}milk_sum. + +DATA gv_region TYPE {{p}}farm-region. + +PARAMETERS p_from TYPE d OBLIGATORY. +PARAMETERS p_to TYPE d OBLIGATORY. +SELECT-OPTIONS s_reg FOR gv_region. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + farm_id TYPE {{p}}farm-farm_id, + farm_name TYPE {{p}}farm-farm_name, + region TYPE {{p}}farm-region, + litres TYPE {{p}}milk-litres, + deliveries TYPE i, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, + tt_region TYPE RANGE OF {{p}}farm-region. + METHODS select_data + IMPORTING iv_from TYPE d + iv_to TYPE d + it_region TYPE tt_region + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_sum, + farm_id TYPE {{p}}milk-farm_id, + litres TYPE {{p}}milk-litres, + deliveries TYPE i, + END OF ty_sum, + tt_sum TYPE HASHED TABLE OF ty_sum WITH UNIQUE KEY farm_id. + METHODS sum_deliveries + IMPORTING iv_from TYPE d + iv_to TYPE d + RETURNING VALUE(rt_sum) TYPE tt_sum. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_data. + SELECT farm_id, farm_name, region + FROM {{p}}farm + WHERE region IN @it_region + INTO TABLE @DATA(lt_farm). + + DATA(lt_sum) = sum_deliveries( iv_from = iv_from iv_to = iv_to ). + + LOOP AT lt_farm INTO DATA(ls_farm). + DATA(ls_line) = VALUE ty_line( farm_id = ls_farm-farm_id + farm_name = ls_farm-farm_name + region = ls_farm-region ). + READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY farm_id = ls_farm-farm_id. + IF sy-subrc = 0. + ls_line-litres = -litres. + ls_line-deliveries = -deliveries. + ENDIF. + APPEND ls_line TO rt_lines. + ENDLOOP. + + SORT rt_lines BY litres DESCENDING farm_id ASCENDING. + ENDMETHOD. + + METHOD sum_deliveries. + SELECT farm_id, litres + FROM {{p}}milk + WHERE deliv_date BETWEEN @iv_from AND @iv_to + INTO TABLE @DATA(lt_milk). + + LOOP AT lt_milk INTO DATA(ls_milk). + READ TABLE rt_sum ASSIGNING FIELD-SYMBOL() + WITH TABLE KEY farm_id = ls_milk-farm_id. + IF sy-subrc <> 0. + DATA(ls_new) = VALUE ty_sum( farm_id = ls_milk-farm_id ). + INSERT ls_new INTO TABLE rt_sum ASSIGNING . + ENDIF. + -litres += ls_milk-litres. + -deliveries += 1. + ENDLOOP. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_data( iv_from = p_from iv_to = p_to it_region = s_reg[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS sums_and_counts FOR TESTING. + METHODS missing_delivery FOR TESTING. + METHODS date_range FOR TESTING. + METHODS tie_break FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}FARM' ) ( '{{P}}MILK' ) ) ). + DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY. + lt_farm = VALUE #( ( farm_id = '1' farm_name = 'A' region = 'X' ) + ( farm_id = '2' farm_name = 'B' region = 'X' ) + ( farm_id = '3' farm_name = 'C' region = 'Y' ) + ( farm_id = '4' farm_name = 'D' region = 'Y' ) + ( farm_id = '5' farm_name = 'E' region = 'X' ) ). + go_env->insert_test_data( lt_farm ). + DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY. + lt_milk = VALUE #( ( deliv_id = 1 farm_id = '1' deliv_date = '20260110' litres = 10 ) + ( deliv_id = 2 farm_id = '1' deliv_date = '20260120' litres = 5 ) + ( deliv_id = 3 farm_id = '2' deliv_date = '20260115' litres = 40 ) + ( deliv_id = 4 farm_id = '3' deliv_date = '20260116' litres = 20 ) + ( deliv_id = 5 farm_id = '5' deliv_date = '20260111' litres = 15 ) ). + go_env->insert_test_data( lt_milk ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD sums_and_counts. + DATA(lt_lines) = NEW lcl_report( )->select_data( + iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '2' act = lt_lines[ 1 ]-farm_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 40 ) act = lt_lines[ 1 ]-litres ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 1 ]-deliveries ). + ENDMETHOD. + + METHOD missing_delivery. + DATA(lt_lines) = NEW lcl_report( )->select_data( + iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = '4' act = lt_lines[ 5 ]-farm_id ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 5 ]-deliveries ). + cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 5 ]-litres ). + ENDMETHOD. + + METHOD date_range. + DATA(lt_lines) = NEW lcl_report( )->select_data( + iv_from = '20260115' iv_to = '20260131' it_region = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = '2' act = lt_lines[ 1 ]-farm_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 40 ) act = lt_lines[ 1 ]-litres ). + cl_abap_unit_assert=>assert_equals( exp = '1' act = lt_lines[ 3 ]-farm_id ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 5 ) act = lt_lines[ 3 ]-litres ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 3 ]-deliveries ). + ENDMETHOD. + + METHOD tie_break. + DATA(lt_lines) = NEW lcl_report( )->select_data( + iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = '1' act = lt_lines[ 3 ]-farm_id ). + cl_abap_unit_assert=>assert_equals( exp = '5' act = lt_lines[ 4 ]-farm_id ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0124/seed/farm.tabl.asabap b/tasks_gen/eval/G0124/seed/farm.tabl.asabap new file mode 100644 index 0000000..d5b8026 --- /dev/null +++ b/tasks_gen/eval/G0124/seed/farm.tabl.asabap @@ -0,0 +1,11 @@ +@EndUserText.label : 'Farm' +@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; + farm_name : abap.char(40); + region : abap.char(4); +} diff --git a/tasks_gen/eval/G0124/seed/milk.tabl.asabap b/tasks_gen/eval/G0124/seed/milk.tabl.asabap new file mode 100644 index 0000000..2dc611f --- /dev/null +++ b/tasks_gen/eval/G0124/seed/milk.tabl.asabap @@ -0,0 +1,12 @@ +@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 deliv_id : abap.int4 not null; + farm_id : abap.char(10); + deliv_date : abap.dats; + litres : abap.dec(13,2); +} diff --git a/tasks_gen/eval/G0124/seed/t20_seed.clas.abap b/tasks_gen/eval/G0124/seed/t20_seed.clas.abap new file mode 100644 index 0000000..de7a249 --- /dev/null +++ b/tasks_gen/eval/G0124/seed/t20_seed.clas.abap @@ -0,0 +1,26 @@ +CLASS {{p}}t20_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}t20_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}milk. + DELETE FROM {{p}}farm. + INSERT {{p}}farm FROM TABLE @( VALUE #( + ( farm_id = '1000' farm_name = 'Alpine Dairy' region = 'CH' ) + ( farm_id = '1001' farm_name = 'Valley Farm' region = 'CH' ) + ( farm_id = '1002' farm_name = 'Lakeside Farm' region = 'DE' ) + ( farm_id = '1003' farm_name = 'Hilltop Farm' region = 'AT' ) + ( farm_id = '1004' farm_name = 'Sunny Acres' region = 'DE' ) ) ). + INSERT {{p}}milk FROM TABLE @( VALUE #( + ( deliv_id = 1 farm_id = '1000' deliv_date = '20260105' litres = '120.50' ) + ( deliv_id = 2 farm_id = '1000' deliv_date = '20260112' litres = '80.00' ) + ( deliv_id = 3 farm_id = '1001' deliv_date = '20260106' litres = '200.00' ) + ( deliv_id = 4 farm_id = '1002' deliv_date = '20260107' litres = '50.25' ) + ( deliv_id = 5 farm_id = '1003' deliv_date = '20251220' litres = '300.00' ) + ( deliv_id = 6 farm_id = '1001' deliv_date = '20260215' litres = '40.00' ) + ( deliv_id = 7 farm_id = '1004' deliv_date = '20260108' litres = '50.25' ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0124/spec.md b/tasks_gen/eval/G0124/spec.md new file mode 100644 index 0000000..8adaffc --- /dev/null +++ b/tasks_gen/eval/G0124/spec.md @@ -0,0 +1,47 @@ +# 1. Goal +Show the milk deliveries of each farm in an ALV list. The cooperative uses the +list to compare the farms of a region for a period. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}FARM exists in package $TMP. It contains data. + Fields: FARM_ID (CHAR 10, key), FARM_NAME (CHAR 40), REGION (CHAR 4). +- Table {{P}}MILK exists in package $TMP. It contains data. + Fields: DELIV_ID (INT4, key), FARM_ID (CHAR 10), DELIV_DATE (DATS), + LITRES (DEC 13,2). + +# 4. Contract +- Create the executable program {{P}}MILK_SUM in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first delivery date to include. + - Parameter P_TO, type DATS, mandatory: the last delivery date to include. + - Select-option S_REG for the region. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: FARM_ID, FARM_NAME, REGION, LITRES, DELIVERIES. + +# 5. Business rules +1. Include a delivery only if DELIV_DATE is on or after P_FROM and on or before + P_TO. Both limits are included. +2. Include a farm only if REGION is in S_REG. An empty S_REG includes every + region. A line with sign E in S_REG excludes its region. +3. Show one line for each included farm. A farm without an included delivery also + gets a line. +4. LITRES is the sum of the litres of the included deliveries of the farm. LITRES + is 0 if the farm has no included delivery. +5. DELIVERIES is the number of included deliveries of the farm. DELIVERIES is 0 + if the farm has no included delivery. +6. Sort the lines by LITRES, the highest value first. If two lines have the same + LITRES, sort them by FARM_ID, the lowest value first. +7. FARM_NAME and REGION are the values of the farm. + +# 6. Constraints +- Release target: 7.58. +- Coding standards: Clean ABAP. Read each table with one database access. +- Out of scope: do not change {{P}}FARM and {{P}}MILK. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/eval/G0124/task.json b/tasks_gen/eval/G0124/task.json new file mode 100644 index 0000000..5a26ce6 --- /dev/null +++ b/tasks_gen/eval/G0124/task.json @@ -0,0 +1,68 @@ +{ + "id": "G0124", + "category": "C", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v758", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}FARM", + "file": "seed/farm.tabl.asabap", + "description": "Farms of the cooperative" + }, + { + "type": "TABL", + "name": "{{P}}MILK", + "file": "seed/milk.tabl.asabap", + "description": "Milk deliveries" + }, + { + "type": "CLAS", + "name": "{{P}}T20_SEED", + "file": "seed/t20_seed.clas.abap", + "description": "T20 seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}MILK_SUM", + "parameters": [ + "p_from", + "p_to", + "s_reg" + ] + } + ], + "out_of_scope": [ + "{{P}}FARM", + "{{P}}MILK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T20_HIDDEN", + "file": "hidden/t20_hidden.clas.abap", + "description": "T20 hidden tests" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}MILK_SUM", + "file": "reference/milk_sum.prog.abap", + "description": "Milk litres per farm" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0125/faulty/m0_flight_sum.prog.abap b/tasks_gen/eval/G0125/faulty/m0_flight_sum.prog.abap new file mode 100644 index 0000000..395cb82 --- /dev/null +++ b/tasks_gen/eval/G0125/faulty/m0_flight_sum.prog.abap @@ -0,0 +1,186 @@ +REPORT {{p}}flight_sum. + +DATA gv_dest TYPE {{p}}flight-dest. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_dest FOR gv_dest. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + dest TYPE {{p}}flight-dest, + name TYPE {{p}}airport-name, + flights TYPE i, + pax TYPE {{p}}flight-pax, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_dest TYPE RANGE OF {{p}}flight-dest. + METHODS select_lines + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_total, + dest TYPE {{p}}flight-dest, + flight_count TYPE i, + pax_sum TYPE {{p}}flight-pax, + END OF ty_total, + tt_total TYPE STANDARD TABLE OF ty_total WITH EMPTY KEY. + TYPES: + BEGIN OF ty_name, + airport TYPE {{p}}airport-airport, + name TYPE {{p}}airport-name, + END OF ty_name, + tt_name TYPE SORTED TABLE OF ty_name WITH UNIQUE KEY airport. + METHODS read_totals + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_totals) TYPE tt_total. + METHODS read_names + IMPORTING it_dest TYPE tt_dest + RETURNING VALUE(rt_names) TYPE tt_name. + METHODS build_lines + IMPORTING it_totals TYPE tt_total + it_names TYPE tt_name + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_lines. + DATA(lt_totals) = read_totals( iv_from = iv_from it_dest = it_dest ). + rt_lines = build_lines( it_totals = lt_totals it_names = read_names( it_dest = it_dest ) ). + ENDMETHOD. + + METHOD read_totals. + SELECT dest, COUNT( * ) AS flight_count, SUM( pax ) AS pax_sum + FROM {{p}}flight + WHERE dest IN @it_dest + OR flight_date >= @iv_from + GROUP BY dest + ORDER BY pax_sum DESCENDING, dest ASCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_totals. + ENDMETHOD. + + METHOD read_names. + SELECT airport, name + FROM {{p}}airport + WHERE airport IN @it_dest + INTO CORRESPONDING FIELDS OF TABLE @rt_names. + ENDMETHOD. + + METHOD build_lines. + LOOP AT it_totals INTO DATA(ls_total). + DATA(ls_line) = VALUE ty_line( dest = ls_total-dest + flights = ls_total-flight_count + pax = ls_total-pax_sum ). + READ TABLE it_names WITH TABLE KEY airport = ls_total-dest INTO DATA(ls_name). + IF sy-subrc = 0. + ls_line-name = ls_name-name. + ELSE. + ls_line-name = 'UNKNOWN'. + ENDIF. + APPEND ls_line TO rt_lines. + ENDLOOP. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_lines( iv_from = p_from it_dest = s_dest[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_dest FOR TESTING. + METHODS sorted_by_pax FOR TESTING. + METHODS tie_break_by_dest FOR TESTING. + METHODS airport_names FOR TESTING. + METHODS unknown_name FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS dest_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}FLIGHT' ) ( '{{P}}AIRPORT' ) ) ). + DATA lt_flight TYPE STANDARD TABLE OF {{p}}flight WITH EMPTY KEY. + lt_flight = VALUE #( ( flight_id = 1 dest = 'AAA' flight_date = '20260110' pax = 100 ) + ( flight_id = 2 dest = 'AAA' flight_date = '20260210' pax = 50 ) + ( flight_id = 3 dest = 'BBB' flight_date = '20260115' pax = 300 ) + ( flight_id = 4 dest = 'CCC' flight_date = '20260120' pax = 150 ) ). + go_env->insert_test_data( lt_flight ). + DATA lt_airport TYPE STANDARD TABLE OF {{p}}airport WITH EMPTY KEY. + lt_airport = VALUE #( ( airport = 'AAA' name = 'ALPHA' ) ). + go_env->insert_test_data( lt_airport ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 1 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 300 ) act = lt_lines[ 1 ]-pax ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 150 ) act = lt_lines[ 2 ]-pax ). + ENDMETHOD. + + METHOD sorted_by_pax. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD tie_break_by_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 2 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD airport_names. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_lines[ 2 ]-name ). + ENDMETHOD. + + METHOD unknown_name. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 1 ]-name ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 3 ]-name ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260201' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 50 ) act = lt_lines[ 1 ]-pax ). + ENDMETHOD. + + METHOD dest_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( + iv_from = '20260101' it_dest = VALUE #( ( sign = 'I' option = 'EQ' low = 'AAA' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-flights ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0125/faulty/m1_flight_sum.prog.abap b/tasks_gen/eval/G0125/faulty/m1_flight_sum.prog.abap new file mode 100644 index 0000000..1be63d2 --- /dev/null +++ b/tasks_gen/eval/G0125/faulty/m1_flight_sum.prog.abap @@ -0,0 +1,186 @@ +REPORT {{p}}flight_sum. + +DATA gv_dest TYPE {{p}}flight-dest. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_dest FOR gv_dest. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + dest TYPE {{p}}flight-dest, + name TYPE {{p}}airport-name, + flights TYPE i, + pax TYPE {{p}}flight-pax, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_dest TYPE RANGE OF {{p}}flight-dest. + METHODS select_lines + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_total, + dest TYPE {{p}}flight-dest, + flight_count TYPE i, + pax_sum TYPE {{p}}flight-pax, + END OF ty_total, + tt_total TYPE STANDARD TABLE OF ty_total WITH EMPTY KEY. + TYPES: + BEGIN OF ty_name, + airport TYPE {{p}}airport-airport, + name TYPE {{p}}airport-name, + END OF ty_name, + tt_name TYPE SORTED TABLE OF ty_name WITH UNIQUE KEY airport. + METHODS read_totals + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_totals) TYPE tt_total. + METHODS read_names + IMPORTING it_dest TYPE tt_dest + RETURNING VALUE(rt_names) TYPE tt_name. + METHODS build_lines + IMPORTING it_totals TYPE tt_total + it_names TYPE tt_name + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_lines. + DATA(lt_totals) = read_totals( iv_from = iv_from it_dest = it_dest ). + rt_lines = build_lines( it_totals = lt_totals it_names = read_names( it_dest = it_dest ) ). + ENDMETHOD. + + METHOD read_totals. + SELECT dest, COUNT( * ) AS flight_count, SUM( pax ) AS pax_sum + FROM {{p}}flight + WHERE dest IN @it_dest + AND flight_date < @iv_from + GROUP BY dest + ORDER BY pax_sum DESCENDING, dest ASCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_totals. + ENDMETHOD. + + METHOD read_names. + SELECT airport, name + FROM {{p}}airport + WHERE airport IN @it_dest + INTO CORRESPONDING FIELDS OF TABLE @rt_names. + ENDMETHOD. + + METHOD build_lines. + LOOP AT it_totals INTO DATA(ls_total). + DATA(ls_line) = VALUE ty_line( dest = ls_total-dest + flights = ls_total-flight_count + pax = ls_total-pax_sum ). + READ TABLE it_names WITH TABLE KEY airport = ls_total-dest INTO DATA(ls_name). + IF sy-subrc = 0. + ls_line-name = ls_name-name. + ELSE. + ls_line-name = 'UNKNOWN'. + ENDIF. + APPEND ls_line TO rt_lines. + ENDLOOP. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_lines( iv_from = p_from it_dest = s_dest[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_dest FOR TESTING. + METHODS sorted_by_pax FOR TESTING. + METHODS tie_break_by_dest FOR TESTING. + METHODS airport_names FOR TESTING. + METHODS unknown_name FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS dest_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}FLIGHT' ) ( '{{P}}AIRPORT' ) ) ). + DATA lt_flight TYPE STANDARD TABLE OF {{p}}flight WITH EMPTY KEY. + lt_flight = VALUE #( ( flight_id = 1 dest = 'AAA' flight_date = '20260110' pax = 100 ) + ( flight_id = 2 dest = 'AAA' flight_date = '20260210' pax = 50 ) + ( flight_id = 3 dest = 'BBB' flight_date = '20260115' pax = 300 ) + ( flight_id = 4 dest = 'CCC' flight_date = '20260120' pax = 150 ) ). + go_env->insert_test_data( lt_flight ). + DATA lt_airport TYPE STANDARD TABLE OF {{p}}airport WITH EMPTY KEY. + lt_airport = VALUE #( ( airport = 'AAA' name = 'ALPHA' ) ). + go_env->insert_test_data( lt_airport ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 1 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 300 ) act = lt_lines[ 1 ]-pax ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 150 ) act = lt_lines[ 2 ]-pax ). + ENDMETHOD. + + METHOD sorted_by_pax. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD tie_break_by_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 2 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD airport_names. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_lines[ 2 ]-name ). + ENDMETHOD. + + METHOD unknown_name. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 1 ]-name ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 3 ]-name ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260201' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 50 ) act = lt_lines[ 1 ]-pax ). + ENDMETHOD. + + METHOD dest_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( + iv_from = '20260101' it_dest = VALUE #( ( sign = 'I' option = 'EQ' low = 'AAA' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-flights ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0125/generation.json b/tasks_gen/eval/G0125/generation.json new file mode 100644 index 0000000..f152f4b --- /dev/null +++ b/tasks_gen/eval/G0125/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0125", + "pool": "eval", + "object_type": "PROG", + "category": "C", + "attempts": [ + { + "stage": "validate", + "oracle": 95.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 95.0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 2, + "killed": 2, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0125/hidden/flight_hidden.clas.abap b/tasks_gen/eval/G0125/hidden/flight_hidden.clas.abap new file mode 100644 index 0000000..4c06655 --- /dev/null +++ b/tasks_gen/eval/G0125/hidden/flight_hidden.clas.abap @@ -0,0 +1,99 @@ +CLASS {{p}}flight_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES tt_dest TYPE RANGE OF {{p}}flight-dest. + METHODS run + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest OPTIONAL + RETURNING VALUE(rr_data) TYPE REF TO data. + METHODS value_of + IMPORTING ir_data TYPE REF TO data + iv_row TYPE i + iv_col TYPE string + RETURNING VALUE(rv_value) TYPE string. + METHODS lines_of + IMPORTING ir_data TYPE REF TO data + RETURNING VALUE(rv_lines) TYPE i. + METHODS all_destinations FOR TESTING. + METHODS sorted_by_pax FOR TESTING. + METHODS tie_break_by_dest FOR TESTING. + METHODS counts_and_sums FOR TESTING. + METHODS airport_names FOR TESTING. + METHODS unknown_airport FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS dest_filter FOR TESTING. +ENDCLASS. + + +CLASS {{p}}flight_hidden IMPLEMENTATION. + METHOD run. + cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ). + SUBMIT {{p}}flight_sum WITH p_from = iv_from WITH s_dest IN it_dest AND RETURN. + TRY. + cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ). + CATCH cx_salv_bs_sc_runtime_info. + ENDTRY. + cl_salv_bs_runtime_info=>clear_all( ). + ENDMETHOD. + + METHOD lines_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + IF ir_data IS BOUND. + ASSIGN ir_data->* TO . + rv_lines = lines( ). + ENDIF. + ENDMETHOD. + + METHOD value_of. + FIELD-SYMBOLS TYPE STANDARD TABLE. + ASSIGN ir_data->* TO . + ASSIGN [ iv_row ] TO FIELD-SYMBOL(). + ASSIGN COMPONENT iv_col OF STRUCTURE TO FIELD-SYMBOL(). + cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ). + rv_value = condense( CONV string( ) ). + ENDMETHOD. + + METHOD all_destinations. + cl_abap_unit_assert=>assert_equals( exp = 4 act = lines_of( run( '20260101' ) ) ). + ENDMETHOD. + + METHOD sorted_by_pax. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `BBB` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DEST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `CCC` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `DEST` ) ). + ENDMETHOD. + + METHOD tie_break_by_dest. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `AAA` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DEST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `DDD` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DEST` ) ). + ENDMETHOD. + + METHOD counts_and_sums. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `BBB` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DEST` ) ). + cl_abap_unit_assert=>assert_equals( exp = `2` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FLIGHTS` ) ). + cl_abap_unit_assert=>assert_equals( exp = `400` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PAX` ) ). + ENDMETHOD. + + METHOD airport_names. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `ALPHA` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `NAME` ) ). + ENDMETHOD. + + METHOD unknown_airport. + DATA(lr_data) = run( '20260101' ). + cl_abap_unit_assert=>assert_equals( exp = `UNKNOWN` act = value_of( ir_data = lr_data iv_row = 4 iv_col = `NAME` ) ). + ENDMETHOD. + + METHOD date_filter. + cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( run( '20260201' ) ) ). + ENDMETHOD. + + METHOD dest_filter. + DATA(lr_data) = run( iv_from = '20260101' + it_dest = VALUE #( ( sign = 'I' option = 'EQ' low = 'BBB' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ). + cl_abap_unit_assert=>assert_equals( exp = `400` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `PAX` ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0125/mutation.json b/tasks_gen/eval/G0125/mutation.json new file mode 100644 index 0000000..6300598 --- /dev/null +++ b/tasks_gen/eval/G0125/mutation.json @@ -0,0 +1,40 @@ +{ + "task": "G0125", + "mutants": [ + { + "object": "{{P}}FLIGHT_SUM", + "mutant": "line 62: AND -> OR (logic)", + "status": "killed", + "hidden": "1/8", + "failed_tests": [ + "AIRPORT_NAMES", + "COUNTS_AND_SUMS", + "DATE_FILTER", + "DEST_FILTER", + "SORTED_BY_PAX", + "TIE_BREAK_BY_DEST", + "UNKNOWN_AIRPORT" + ] + }, + { + "object": "{{P}}FLIGHT_SUM", + "mutant": "line 62: >= -> < (rel)", + "status": "killed", + "hidden": "0/8", + "failed_tests": [ + "AIRPORT_NAMES", + "ALL_DESTINATIONS", + "COUNTS_AND_SUMS", + "DATE_FILTER", + "DEST_FILTER", + "SORTED_BY_PAX", + "TIE_BREAK_BY_DEST", + "UNKNOWN_AIRPORT" + ] + } + ], + "valid": 2, + "killed": 2, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0125/reference/flight_sum.prog.abap b/tasks_gen/eval/G0125/reference/flight_sum.prog.abap new file mode 100644 index 0000000..26ba2b3 --- /dev/null +++ b/tasks_gen/eval/G0125/reference/flight_sum.prog.abap @@ -0,0 +1,186 @@ +REPORT {{p}}flight_sum. + +DATA gv_dest TYPE {{p}}flight-dest. + +PARAMETERS p_from TYPE d OBLIGATORY. +SELECT-OPTIONS s_dest FOR gv_dest. + +CLASS lcl_report DEFINITION FINAL. + PUBLIC SECTION. + TYPES: + BEGIN OF ty_line, + dest TYPE {{p}}flight-dest, + name TYPE {{p}}airport-name, + flights TYPE i, + pax TYPE {{p}}flight-pax, + END OF ty_line, + tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY. + TYPES tt_dest TYPE RANGE OF {{p}}flight-dest. + METHODS select_lines + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_lines) TYPE tt_line. + METHODS display + CHANGING ct_lines TYPE tt_line. + PRIVATE SECTION. + TYPES: + BEGIN OF ty_total, + dest TYPE {{p}}flight-dest, + flight_count TYPE i, + pax_sum TYPE {{p}}flight-pax, + END OF ty_total, + tt_total TYPE STANDARD TABLE OF ty_total WITH EMPTY KEY. + TYPES: + BEGIN OF ty_name, + airport TYPE {{p}}airport-airport, + name TYPE {{p}}airport-name, + END OF ty_name, + tt_name TYPE SORTED TABLE OF ty_name WITH UNIQUE KEY airport. + METHODS read_totals + IMPORTING iv_from TYPE d + it_dest TYPE tt_dest + RETURNING VALUE(rt_totals) TYPE tt_total. + METHODS read_names + IMPORTING it_dest TYPE tt_dest + RETURNING VALUE(rt_names) TYPE tt_name. + METHODS build_lines + IMPORTING it_totals TYPE tt_total + it_names TYPE tt_name + RETURNING VALUE(rt_lines) TYPE tt_line. +ENDCLASS. + +CLASS lcl_report IMPLEMENTATION. + METHOD select_lines. + DATA(lt_totals) = read_totals( iv_from = iv_from it_dest = it_dest ). + rt_lines = build_lines( it_totals = lt_totals it_names = read_names( it_dest = it_dest ) ). + ENDMETHOD. + + METHOD read_totals. + SELECT dest, COUNT( * ) AS flight_count, SUM( pax ) AS pax_sum + FROM {{p}}flight + WHERE dest IN @it_dest + AND flight_date >= @iv_from + GROUP BY dest + ORDER BY pax_sum DESCENDING, dest ASCENDING + INTO CORRESPONDING FIELDS OF TABLE @rt_totals. + ENDMETHOD. + + METHOD read_names. + SELECT airport, name + FROM {{p}}airport + WHERE airport IN @it_dest + INTO CORRESPONDING FIELDS OF TABLE @rt_names. + ENDMETHOD. + + METHOD build_lines. + LOOP AT it_totals INTO DATA(ls_total). + DATA(ls_line) = VALUE ty_line( dest = ls_total-dest + flights = ls_total-flight_count + pax = ls_total-pax_sum ). + READ TABLE it_names WITH TABLE KEY airport = ls_total-dest INTO DATA(ls_name). + IF sy-subrc = 0. + ls_line-name = ls_name-name. + ELSE. + ls_line-name = 'UNKNOWN'. + ENDIF. + APPEND ls_line TO rt_lines. + ENDLOOP. + ENDMETHOD. + + METHOD display. + TRY. + cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) + CHANGING t_table = ct_lines ). + lo_alv->display( ). + CATCH cx_salv_msg INTO DATA(lx_error). + MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. + ENDTRY. + ENDMETHOD. +ENDCLASS. + +START-OF-SELECTION. + DATA(go_report) = NEW lcl_report( ). + DATA(gt_lines) = go_report->select_lines( iv_from = p_from it_dest = s_dest[] ). + go_report->display( CHANGING ct_lines = gt_lines ). + +CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + CLASS-DATA go_env TYPE REF TO if_osql_test_environment. + CLASS-METHODS class_setup. + CLASS-METHODS class_teardown. + METHODS totals_per_dest FOR TESTING. + METHODS sorted_by_pax FOR TESTING. + METHODS tie_break_by_dest FOR TESTING. + METHODS airport_names FOR TESTING. + METHODS unknown_name FOR TESTING. + METHODS date_filter FOR TESTING. + METHODS dest_filter FOR TESTING. +ENDCLASS. + +CLASS ltc_report IMPLEMENTATION. + METHOD class_setup. + go_env = cl_osql_test_environment=>create( + i_dependency_list = VALUE #( ( '{{P}}FLIGHT' ) ( '{{P}}AIRPORT' ) ) ). + DATA lt_flight TYPE STANDARD TABLE OF {{p}}flight WITH EMPTY KEY. + lt_flight = VALUE #( ( flight_id = 1 dest = 'AAA' flight_date = '20260110' pax = 100 ) + ( flight_id = 2 dest = 'AAA' flight_date = '20260210' pax = 50 ) + ( flight_id = 3 dest = 'BBB' flight_date = '20260115' pax = 300 ) + ( flight_id = 4 dest = 'CCC' flight_date = '20260120' pax = 150 ) ). + go_env->insert_test_data( lt_flight ). + DATA lt_airport TYPE STANDARD TABLE OF {{p}}airport WITH EMPTY KEY. + lt_airport = VALUE #( ( airport = 'AAA' name = 'ALPHA' ) ). + go_env->insert_test_data( lt_airport ). + ENDMETHOD. + + METHOD class_teardown. + go_env->destroy( ). + ENDMETHOD. + + METHOD totals_per_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 1 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 300 ) act = lt_lines[ 1 ]-pax ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-flights ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 150 ) act = lt_lines[ 2 ]-pax ). + ENDMETHOD. + + METHOD sorted_by_pax. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'BBB' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD tie_break_by_dest. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 2 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 'CCC' act = lt_lines[ 3 ]-dest ). + ENDMETHOD. + + METHOD airport_names. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_lines[ 2 ]-name ). + ENDMETHOD. + + METHOD unknown_name. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260101' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 1 ]-name ). + cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN' act = lt_lines[ 3 ]-name ). + ENDMETHOD. + + METHOD date_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( iv_from = '20260201' it_dest = VALUE #( ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}flight-pax( 50 ) act = lt_lines[ 1 ]-pax ). + ENDMETHOD. + + METHOD dest_filter. + DATA(lt_lines) = NEW lcl_report( )->select_lines( + iv_from = '20260101' it_dest = VALUE #( ( sign = 'I' option = 'EQ' low = 'AAA' ) ) ). + cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). + cl_abap_unit_assert=>assert_equals( exp = 'AAA' act = lt_lines[ 1 ]-dest ). + cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 1 ]-flights ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0125/seed/airport.tabl.asabap b/tasks_gen/eval/G0125/seed/airport.tabl.asabap new file mode 100644 index 0000000..0b4795e --- /dev/null +++ b/tasks_gen/eval/G0125/seed/airport.tabl.asabap @@ -0,0 +1,10 @@ +@EndUserText.label : 'Airport' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}airport { + key client : abap.clnt not null; + key airport : abap.char(3) not null; + name : abap.char(40); +} diff --git a/tasks_gen/eval/G0125/seed/flight.tabl.asabap b/tasks_gen/eval/G0125/seed/flight.tabl.asabap new file mode 100644 index 0000000..7b4a163 --- /dev/null +++ b/tasks_gen/eval/G0125/seed/flight.tabl.asabap @@ -0,0 +1,12 @@ +@EndUserText.label : 'Flight movement' +@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE +@AbapCatalog.tableCategory : #TRANSPARENT +@AbapCatalog.deliveryClass : #A +@AbapCatalog.dataMaintenance : #RESTRICTED +define table {{p}}flight { + key client : abap.clnt not null; + key dest : abap.char(3) not null; + key flight_date : abap.dats not null; + key flight_id : abap.int4 not null; + pax : abap.int4; +} diff --git a/tasks_gen/eval/G0125/seed/flight_seed.clas.abap b/tasks_gen/eval/G0125/seed/flight_seed.clas.abap new file mode 100644 index 0000000..dc4f969 --- /dev/null +++ b/tasks_gen/eval/G0125/seed/flight_seed.clas.abap @@ -0,0 +1,24 @@ +CLASS {{p}}flight_seed DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. +ENDCLASS. + +CLASS {{p}}flight_seed IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + DELETE FROM {{p}}flight. + DELETE FROM {{p}}airport. + INSERT {{p}}airport FROM TABLE @( VALUE #( + ( airport = 'AAA' name = 'ALPHA' ) + ( airport = 'BBB' name = 'BETA' ) + ( airport = 'DDD' name = 'DELTA' ) ) ). + INSERT {{p}}flight FROM TABLE @( VALUE #( + ( flight_id = 1 dest = 'AAA' flight_date = '20260110' pax = 100 ) + ( flight_id = 2 dest = 'AAA' flight_date = '20260210' pax = 50 ) + ( flight_id = 3 dest = 'BBB' flight_date = '20260115' pax = 300 ) + ( flight_id = 4 dest = 'CCC' flight_date = '20251231' pax = 500 ) + ( flight_id = 5 dest = 'CCC' flight_date = '20260301' pax = 20 ) + ( flight_id = 6 dest = 'BBB' flight_date = '20260120' pax = 100 ) + ( flight_id = 7 dest = 'DDD' flight_date = '20260125' pax = 150 ) ) ). + out->write( sy-dbcnt ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0125/spec.md b/tasks_gen/eval/G0125/spec.md new file mode 100644 index 0000000..bfcc6b7 --- /dev/null +++ b/tasks_gen/eval/G0125/spec.md @@ -0,0 +1,43 @@ +# 1. Goal +A regional airport collects the movements of its flights. The operations team +needs a report that shows, for each destination, how many flights went there +and how many passengers they carried. + +# 2. Open questions +None. + +# 3. Context +- Table {{P}}FLIGHT exists in package $TMP. It contains data. + Fields: FLIGHT_ID (INT4, key), DEST (CHAR 3), FLIGHT_DATE (DATS), PAX (INT4). +- Table {{P}}AIRPORT exists in package $TMP. It contains data. + Fields: AIRPORT (CHAR 3, key), NAME (CHAR 40). + +# 4. Contract +- Create the executable program {{P}}FLIGHT_SUM in package $TMP. +- Selection screen: + - Parameter P_FROM, type DATS, mandatory: the first flight date to include. + - Select-option S_DEST for the destination. +- Show the result with CL_SALV_TABLE. The output table has these columns, with + these names: DEST, NAME, FLIGHTS (number of flights), PAX (sum of the + passengers). + +# 5. Business rules +1. Include a flight only if FLIGHT_DATE is on or after P_FROM and DEST is in + S_DEST. +2. Show one line for each destination that has at least one included flight. +3. NAME is the airport name of the destination from {{P}}AIRPORT. If the + destination has no entry in {{P}}AIRPORT, NAME is 'UNKNOWN'. +4. FLIGHTS is the number of included flights of the destination. PAX is the sum + of the passengers of the included flights. +5. Sort the lines by PAX, the highest first. If two lines have the same PAX, + sort them by DEST in ascending order. + +# 6. Constraints +- Release target: 8.16. +- Coding standards: Clean ABAP. +- Out of scope: do not change {{P}}FLIGHT and {{P}}AIRPORT. + +# 7. Acceptance +- The program is active and has no syntax error. +- The hidden tests pass. +- Write ABAP Unit tests as local test classes in the program. diff --git a/tasks_gen/eval/G0125/task.json b/tasks_gen/eval/G0125/task.json new file mode 100644 index 0000000..bf93c90 --- /dev/null +++ b/tasks_gen/eval/G0125/task.json @@ -0,0 +1,67 @@ +{ + "id": "G0125", + "category": "C", + "object_type": "PROG", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "TABL", + "name": "{{P}}FLIGHT", + "file": "seed/flight.tabl.asabap", + "description": "Flight movements of the airport" + }, + { + "type": "TABL", + "name": "{{P}}AIRPORT", + "file": "seed/airport.tabl.asabap", + "description": "Airport master data" + }, + { + "type": "CLAS", + "name": "{{P}}FLIGHT_SEED", + "file": "seed/flight_seed.clas.abap", + "description": "Flight and airport seed data", + "run": true + } + ], + "contract": [ + { + "type": "PROG", + "name": "{{P}}FLIGHT_SUM", + "parameters": [ + "p_from", + "s_dest" + ] + } + ], + "out_of_scope": [ + "{{P}}FLIGHT", + "{{P}}AIRPORT" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}FLIGHT_HIDDEN", + "file": "hidden/flight_hidden.clas.abap", + "description": "Hidden tests for the flight report" + } + ], + "reference": [ + { + "type": "PROG", + "name": "{{P}}FLIGHT_SUM", + "file": "reference/flight_sum.prog.abap", + "description": "Flights and passengers per destination" + } + ], + "craft_checks": [ + "db_operation_in_loop", + "method_length" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0126/generation.json b/tasks_gen/eval/G0126/generation.json new file mode 100644 index 0000000..c664d89 --- /dev/null +++ b/tasks_gen/eval/G0126/generation.json @@ -0,0 +1,34 @@ +{ + "id": "G0126", + "pool": "eval", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t_bag_drop_hidden.clas.abap line 85: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"mo_cut\"", + "hidden/t_bag_drop_hidden.clas.abap line 86: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"01\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 92.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 92.5, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/eval/G0126/hidden/t_bag_drop_hidden.clas.abap b/tasks_gen/eval/G0126/hidden/t_bag_drop_hidden.clas.abap new file mode 100644 index 0000000..00631f7 --- /dev/null +++ b/tasks_gen/eval/G0126/hidden/t_bag_drop_hidden.clas.abap @@ -0,0 +1,219 @@ +CLASS {{p}}t_bag_drop_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}bag_drop. + METHODS setup. + METHODS accepts_valid_bag FOR TESTING. + METHODS rejects_bad_tag FOR TESTING. + METHODS accepts_max_weight FOR TESTING. + METHODS rejects_overweight FOR TESTING. + METHODS accepts_max_size FOR TESTING. + METHODS rejects_oversize FOR TESTING. + METHODS accepts_one_bag FOR TESTING. + METHODS rejects_third_bag FOR TESTING. + METHODS accepts_40_minutes FOR TESTING. + METHODS rejects_39_minutes FOR TESTING. + METHODS tag_checked_first FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_bag_drop_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}bag_drop( ). + ENDMETHOD. + + METHOD accepts_valid_bag. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A valid bag was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_bad_tag. + DATA lv_tag TYPE {{p}}bag_drop=>ty_tag. + DATA lo_tag TYPE REF TO {{p}}cx_bag_tag. + TRY. + mo_cut->accept_bag( iv_tag = lv_tag + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An empty tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + TRY. + mo_cut->accept_bag( iv_tag = 'AB1234567' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'A short tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_max_weight. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 23 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the weight limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_overweight. + DATA lv_weight TYPE {{p}}bag_drop=>ty_weight. + DATA lo_weight TYPE REF TO {{p}}cx_bag_weight. + lv_weight = '23.01'. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = lv_weight + iv_length = 100 + iv_width = 100 + iv_height = 100 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An overweight bag was accepted' ). + CATCH {{p}}cx_bag_weight INTO lo_weight. + cl_abap_unit_assert=>assert_equals( exp = `Bag is too heavy` act = lo_weight->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_max_size. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 70 + iv_width = 50 + iv_height = 38 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the size limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_oversize. + DATA lo_size TYPE REF TO {{p}}cx_bag_size. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 70 + iv_width = 50 + iv_height = 39 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An oversize bag was accepted' ). + CATCH {{p}}cx_bag_size INTO lo_size. + cl_abap_unit_assert=>assert_equals( exp = `Bag is too large` act = lo_size->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_one_bag. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 1 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A second bag was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_third_bag. + DATA lo_limit TYPE REF TO {{p}}cx_bag_limit. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 2 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'A third bag was accepted' ). + CATCH {{p}}cx_bag_limit INTO lo_limit. + cl_abap_unit_assert=>assert_equals( exp = `Bag limit reached` act = lo_limit->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_40_minutes. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 40 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the time limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_39_minutes. + DATA lo_late TYPE REF TO {{p}}cx_bag_late. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 39 ). + cl_abap_unit_assert=>fail( msg = 'A late bag was accepted' ). + CATCH {{p}}cx_bag_late INTO lo_late. + cl_abap_unit_assert=>assert_equals( exp = `Check-in closed` act = lo_late->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD tag_checked_first. + DATA lo_tag TYPE REF TO {{p}}cx_bag_tag. + TRY. + mo_cut->accept_bag( iv_tag = 'ab12345678' + iv_weight = 30 + iv_length = 200 + iv_width = 100 + iv_height = 100 + iv_bags_checked = 5 + iv_min_to_dep = 10 ). + cl_abap_unit_assert=>fail( msg = 'A bag with a bad tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/bag_drop.clas.abap b/tasks_gen/eval/G0126/reference/bag_drop.clas.abap new file mode 100644 index 0000000..bab6da4 --- /dev/null +++ b/tasks_gen/eval/G0126/reference/bag_drop.clas.abap @@ -0,0 +1,93 @@ +CLASS {{p}}bag_drop DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_tag TYPE c LENGTH 10, + ty_weight TYPE p LENGTH 5 DECIMALS 2, + ty_size TYPE i, + ty_count TYPE i, + ty_minutes TYPE i. + + METHODS accept_bag + IMPORTING + iv_tag TYPE ty_tag + iv_weight TYPE ty_weight + iv_length TYPE ty_size + iv_width TYPE ty_size + iv_height TYPE ty_size + iv_bags_checked TYPE ty_count + iv_min_to_dep TYPE ty_minutes + RAISING + {{p}}cx_bag_tag + {{p}}cx_bag_weight + {{p}}cx_bag_size + {{p}}cx_bag_limit + {{p}}cx_bag_late. + + PRIVATE SECTION. + CONSTANTS: + c_max_weight TYPE ty_weight VALUE 23, + c_max_size TYPE ty_size VALUE 158, + c_max_bags TYPE ty_count VALUE 2, + c_min_minutes TYPE ty_minutes VALUE 40. + + METHODS check_tag + IMPORTING iv_tag TYPE ty_tag + RAISING {{p}}cx_bag_tag. + METHODS check_weight + IMPORTING iv_weight TYPE ty_weight + RAISING {{p}}cx_bag_weight. + METHODS check_size + IMPORTING iv_length TYPE ty_size + iv_width TYPE ty_size + iv_height TYPE ty_size + RAISING {{p}}cx_bag_size. + METHODS check_bag_count + IMPORTING iv_bags_checked TYPE ty_count + RAISING {{p}}cx_bag_limit. + METHODS check_departure + IMPORTING iv_min_to_dep TYPE ty_minutes + RAISING {{p}}cx_bag_late. +ENDCLASS. + + +CLASS {{p}}bag_drop IMPLEMENTATION. + METHOD accept_bag. + check_tag( iv_tag ). + check_weight( iv_weight ). + check_size( iv_length = iv_length + iv_width = iv_width + iv_height = iv_height ). + check_bag_count( iv_bags_checked ). + check_departure( iv_min_to_dep ). + ENDMETHOD. + + METHOD check_tag. + IF iv_tag CN '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + RAISE EXCEPTION TYPE {{p}}cx_bag_tag. + ENDIF. + ENDMETHOD. + + METHOD check_weight. + IF iv_weight > c_max_weight. + RAISE EXCEPTION TYPE {{p}}cx_bag_weight. + ENDIF. + ENDMETHOD. + + METHOD check_size. + IF iv_length + iv_width + iv_height > c_max_size. + RAISE EXCEPTION TYPE {{p}}cx_bag_size. + ENDIF. + ENDMETHOD. + + METHOD check_bag_count. + IF iv_bags_checked >= c_max_bags. + RAISE EXCEPTION TYPE {{p}}cx_bag_limit. + ENDIF. + ENDMETHOD. + + METHOD check_departure. + IF iv_min_to_dep < c_min_minutes. + RAISE EXCEPTION TYPE {{p}}cx_bag_late. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/bag_drop.testclasses.abap b/tasks_gen/eval/G0126/reference/bag_drop.testclasses.abap new file mode 100644 index 0000000..767106a --- /dev/null +++ b/tasks_gen/eval/G0126/reference/bag_drop.testclasses.abap @@ -0,0 +1,218 @@ +CLASS ltc_bag_drop DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}bag_drop. + METHODS setup. + METHODS accepts_valid_bag FOR TESTING. + METHODS rejects_bad_tag FOR TESTING. + METHODS accepts_max_weight FOR TESTING. + METHODS rejects_overweight FOR TESTING. + METHODS accepts_max_size FOR TESTING. + METHODS rejects_oversize FOR TESTING. + METHODS accepts_one_bag FOR TESTING. + METHODS rejects_third_bag FOR TESTING. + METHODS accepts_40_minutes FOR TESTING. + METHODS rejects_39_minutes FOR TESTING. + METHODS tag_checked_first FOR TESTING. +ENDCLASS. + + +CLASS ltc_bag_drop IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}bag_drop( ). + ENDMETHOD. + + METHOD accepts_valid_bag. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A valid bag was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_bad_tag. + DATA lv_tag TYPE {{p}}bag_drop=>ty_tag. + DATA lo_tag TYPE REF TO {{p}}cx_bag_tag. + TRY. + mo_cut->accept_bag( iv_tag = lv_tag + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An empty tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + TRY. + mo_cut->accept_bag( iv_tag = 'AB-2345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'A bag with a bad tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_max_weight. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 23 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the weight limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_overweight. + DATA lv_weight TYPE {{p}}bag_drop=>ty_weight. + DATA lo_weight TYPE REF TO {{p}}cx_bag_weight. + lv_weight = '23.01'. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = lv_weight + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An overweight bag was accepted' ). + CATCH {{p}}cx_bag_weight INTO lo_weight. + cl_abap_unit_assert=>assert_equals( exp = `Bag is too heavy` act = lo_weight->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_max_size. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 70 + iv_width = 50 + iv_height = 38 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the size limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_oversize. + DATA lo_size TYPE REF TO {{p}}cx_bag_size. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 70 + iv_width = 50 + iv_height = 39 + iv_bags_checked = 0 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'An oversize bag was accepted' ). + CATCH {{p}}cx_bag_size INTO lo_size. + cl_abap_unit_assert=>assert_equals( exp = `Bag is too large` act = lo_size->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_one_bag. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 1 + iv_min_to_dep = 90 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A second bag was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_third_bag. + DATA lo_limit TYPE REF TO {{p}}cx_bag_limit. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 2 + iv_min_to_dep = 90 ). + cl_abap_unit_assert=>fail( msg = 'A third bag was accepted' ). + CATCH {{p}}cx_bag_limit INTO lo_limit. + cl_abap_unit_assert=>assert_equals( exp = `Bag limit reached` act = lo_limit->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD accepts_40_minutes. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 40 ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'A bag at the time limit was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_39_minutes. + DATA lo_late TYPE REF TO {{p}}cx_bag_late. + TRY. + mo_cut->accept_bag( iv_tag = 'AB12345678' + iv_weight = 20 + iv_length = 55 + iv_width = 40 + iv_height = 20 + iv_bags_checked = 0 + iv_min_to_dep = 39 ). + cl_abap_unit_assert=>fail( msg = 'A late bag was accepted' ). + CATCH {{p}}cx_bag_late INTO lo_late. + cl_abap_unit_assert=>assert_equals( exp = `Check-in closed` act = lo_late->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD tag_checked_first. + DATA lo_tag TYPE REF TO {{p}}cx_bag_tag. + TRY. + mo_cut->accept_bag( iv_tag = 'ab12345678' + iv_weight = 30 + iv_length = 200 + iv_width = 100 + iv_height = 100 + iv_bags_checked = 5 + iv_min_to_dep = 10 ). + cl_abap_unit_assert=>fail( msg = 'A bag with a bad tag was accepted' ). + CATCH {{p}}cx_bag_tag INTO lo_tag. + cl_abap_unit_assert=>assert_equals( exp = `Invalid bag tag` act = lo_tag->mv_text ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'Wrong exception type' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/cx_bag_late.clas.abap b/tasks_gen/eval/G0126/reference/cx_bag_late.clas.abap new file mode 100644 index 0000000..47cce95 --- /dev/null +++ b/tasks_gen/eval/G0126/reference/cx_bag_late.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_bag_late DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_bag_late IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = 'Check-in closed'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/cx_bag_limit.clas.abap b/tasks_gen/eval/G0126/reference/cx_bag_limit.clas.abap new file mode 100644 index 0000000..7e9e94b --- /dev/null +++ b/tasks_gen/eval/G0126/reference/cx_bag_limit.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_bag_limit DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_bag_limit IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = 'Bag limit reached'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/cx_bag_size.clas.abap b/tasks_gen/eval/G0126/reference/cx_bag_size.clas.abap new file mode 100644 index 0000000..4ed95d8 --- /dev/null +++ b/tasks_gen/eval/G0126/reference/cx_bag_size.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_bag_size DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_bag_size IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = 'Bag is too large'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/cx_bag_tag.clas.abap b/tasks_gen/eval/G0126/reference/cx_bag_tag.clas.abap new file mode 100644 index 0000000..8243013 --- /dev/null +++ b/tasks_gen/eval/G0126/reference/cx_bag_tag.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_bag_tag DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_bag_tag IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = 'Invalid bag tag'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/reference/cx_bag_weight.clas.abap b/tasks_gen/eval/G0126/reference/cx_bag_weight.clas.abap new file mode 100644 index 0000000..6030003 --- /dev/null +++ b/tasks_gen/eval/G0126/reference/cx_bag_weight.clas.abap @@ -0,0 +1,13 @@ +CLASS {{p}}cx_bag_weight DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check. + PUBLIC SECTION. + DATA mv_text TYPE string READ-ONLY. + METHODS constructor. +ENDCLASS. + + +CLASS {{p}}cx_bag_weight IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_text = 'Bag is too heavy'. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0126/spec.md b/tasks_gen/eval/G0126/spec.md new file mode 100644 index 0000000..35d50dc --- /dev/null +++ b/tasks_gen/eval/G0126/spec.md @@ -0,0 +1,93 @@ +# 1. Goal + +A baggage drop-off counter at an airport checks a bag before it accepts the bag. +The counter program calls the check. If the check rejects the bag, the program +shows the reason to the agent. The reason is a class-based exception. + +# 2. Open questions + +None. + +# 3. Context + +- All objects are in package $TMP. +- The check needs only the parameters of the method. No database table and no + helper object is necessary. +- The classes of the contract do not exist yet. + +# 4. Contract + +## 4.1 Class {{P}}BAG_DROP + +- The class is public and final. It has a public constructor without parameters. +- The class has these public types: + - TY_TAG TYPE c LENGTH 10 + - TY_WEIGHT TYPE p LENGTH 5 DECIMALS 2 + - TY_SIZE TYPE i + - TY_COUNT TYPE i + - TY_MINUTES TYPE i +- The class has exactly one public method: + + ACCEPT_BAG + IMPORTING + iv_tag TYPE ty_tag + iv_weight TYPE ty_weight + iv_length TYPE ty_size + iv_width TYPE ty_size + iv_height TYPE ty_size + iv_bags_checked TYPE ty_count + iv_min_to_dep TYPE ty_minutes + RAISING + {{P}}CX_BAG_TAG + {{P}}CX_BAG_WEIGHT + {{P}}CX_BAG_SIZE + {{P}}CX_BAG_LIMIT + {{P}}CX_BAG_LATE + +- Do not add other public methods. + +## 4.2 Exception classes + +Create the classes {{P}}CX_BAG_TAG, {{P}}CX_BAG_WEIGHT, {{P}}CX_BAG_SIZE, +{{P}}CX_BAG_LIMIT and {{P}}CX_BAG_LATE. + +- Each class is public and final. Each class inherits from CX_STATIC_CHECK. +- Each class has a public constructor without parameters. +- Each class has the public read-only attribute MV_TEXT of type string. MV_TEXT + contains the message text of the class. The business rules give the message + texts. + +# 5. Business rules + +The method ACCEPT_BAG checks the bag. It applies the rules in this sequence and +raises the exception of the first rule that fails. It raises no exception if all +rules pass. + +1. Every character of the tag is a digit 0 to 9 or an upper-case letter A to Z. + If not, raise {{P}}CX_BAG_TAG. The message text is "Invalid bag tag". +2. The weight in kg is not greater than 23. If it is greater, raise + {{P}}CX_BAG_WEIGHT. The message text is "Bag is too heavy". +3. The sum of the length, the width and the height in cm is not greater than 158. + If it is greater, raise {{P}}CX_BAG_SIZE. The message text is "Bag is too + large". +4. The number of bags that the passenger already checked in is less than 2. If + it is 2 or more, raise {{P}}CX_BAG_LIMIT. The message text is "Bag limit + reached". +5. The minutes until the departure are 40 or more. If they are less, raise + {{P}}CX_BAG_LATE. The message text is "Check-in closed". + +# 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 the standard classes CX_ROOT and CX_STATIC_CHECK. + +# 7. Acceptance + +- All objects are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class {{P}}BAG_DROP. Your tests cover + every business rule, the exact limits, and the sequence of the rules. Your + tests handle every exception that the method can raise. +- The ATC check of your objects has no finding of priority 1 or 2. diff --git a/tasks_gen/eval/G0126/task.json b/tasks_gen/eval/G0126/task.json new file mode 100644 index 0000000..f16c8b7 --- /dev/null +++ b/tasks_gen/eval/G0126/task.json @@ -0,0 +1,94 @@ +{ + "id": "G0126", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 70, + "max_activations": 15 + }, + "seed": [], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}BAG_DROP" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_TAG" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_WEIGHT" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_SIZE" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_LIMIT" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_LATE" + } + ], + "out_of_scope": [ + "CX_ROOT", + "CX_STATIC_CHECK" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_BAG_DROP_HIDDEN", + "file": "hidden/t_bag_drop_hidden.clas.abap", + "description": "Hidden tests for the baggage acceptance check" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_BAG_TAG", + "file": "reference/cx_bag_tag.clas.abap", + "description": "Invalid bag tag" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_WEIGHT", + "file": "reference/cx_bag_weight.clas.abap", + "description": "Bag is too heavy" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_SIZE", + "file": "reference/cx_bag_size.clas.abap", + "description": "Bag is too large" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_LIMIT", + "file": "reference/cx_bag_limit.clas.abap", + "description": "Bag limit reached" + }, + { + "type": "CLAS", + "name": "{{P}}CX_BAG_LATE", + "file": "reference/cx_bag_late.clas.abap", + "description": "Check-in closed" + }, + { + "type": "CLAS", + "name": "{{P}}BAG_DROP", + "file": "reference/bag_drop.clas.abap", + "description": "Baggage acceptance check", + "testclasses_file": "reference/bag_drop.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "exception_messages" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0127/faulty/m0_ferry_load_check.clas.abap b/tasks_gen/eval/G0127/faulty/m0_ferry_load_check.clas.abap new file mode 100644 index 0000000..7902e96 --- /dev/null +++ b/tasks_gen/eval/G0127/faulty/m0_ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_true. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 OR is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category <> 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/faulty/m1_ferry_load_check.clas.abap b/tasks_gen/eval/G0127/faulty/m1_ferry_load_check.clas.abap new file mode 100644 index 0000000..d80b492 --- /dev/null +++ b/tasks_gen/eval/G0127/faulty/m1_ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) <> abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 OR is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category <> 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/faulty/m2_ferry_load_check.clas.abap b/tasks_gen/eval/G0127/faulty/m2_ferry_load_check.clas.abap new file mode 100644 index 0000000..0b3ad56 --- /dev/null +++ b/tasks_gen/eval/G0127/faulty/m2_ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg > 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 OR is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category <> 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/faulty/m3_ferry_load_check.clas.abap b/tasks_gen/eval/G0127/faulty/m3_ferry_load_check.clas.abap new file mode 100644 index 0000000..c8f06aa --- /dev/null +++ b/tasks_gen/eval/G0127/faulty/m3_ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 AND is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category <> 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/faulty/m4_ferry_load_check.clas.abap b/tasks_gen/eval/G0127/faulty/m4_ferry_load_check.clas.abap new file mode 100644 index 0000000..7993812 --- /dev/null +++ b/tasks_gen/eval/G0127/faulty/m4_ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 OR is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category = 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/files/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0127/files/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..11d51f6 --- /dev/null +++ b/tasks_gen/eval/G0127/files/hidden/t02_hidden.clas.abap @@ -0,0 +1,254 @@ +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}}ferry_load_check. + DATA mo_error TYPE REF TO {{p}}cx_ferry_error. + METHODS setup. + METHODS valid_car_is_ok FOR TESTING. + METHODS valid_truck_hazard_ok FOR TESTING. + METHODS empty_plate_rejected FOR TESTING. + METHODS bad_plate_char_rejected FOR TESTING. + METHODS unknown_category_rejected FOR TESTING. + METHODS zero_weight_rejected FOR TESTING. + METHODS zero_size_rejected FOR TESTING. + METHODS hazard_in_car_rejected FOR TESTING. + METHODS too_high_rejected FOR TESTING. + METHODS weight_limit_rejected FOR TESTING. + METHODS length_limit_rejected FOR TESTING. + METHODS plate_error_wins FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}ferry_load_check( ). + ENDMETHOD. + + METHOD valid_car_is_ok. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'b ab 1234' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>fail( msg = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD valid_truck_hazard_ok. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'HH-TR 900' + category = 'T' + weight_kg = 12000 + length_cm = 1200 + height_cm = 380 + hazard = abap_true ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 20000 + iv_free_length = 5000 + iv_max_height = 400 ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>fail( msg = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD empty_plate_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = space + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'empty plate accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-plate + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD bad_plate_char_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB#123' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'bad plate accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-plate + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD unknown_category_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'X' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'unknown category accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-category + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD zero_weight_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 0 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'zero weight accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-weight + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD zero_size_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 0 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'zero size accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-size + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD hazard_in_car_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_true ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'hazard in car accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-hazard + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD too_high_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 250 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'too high accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-height + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD weight_limit_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 6000 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'weight limit accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-weight_limit + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD length_limit_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 2500 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'length limit accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-length_limit + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD plate_error_wins. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = space + category = 'C' + weight_kg = 9999 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 1000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'empty plate accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-plate + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/files/reference/cx_ferry_error.clas.abap b/tasks_gen/eval/G0127/files/reference/cx_ferry_error.clas.abap new file mode 100644 index 0000000..152ada1 --- /dev/null +++ b/tasks_gen/eval/G0127/files/reference/cx_ferry_error.clas.abap @@ -0,0 +1,15 @@ +CLASS {{p}}cx_ferry_error DEFINITION PUBLIC INHERITING FROM cx_static_check FINAL CREATE PUBLIC. + PUBLIC SECTION. + DATA reason TYPE {{p}}if_ferry_reason=>ty_reason READ-ONLY. + + METHODS constructor + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason. +ENDCLASS. + + +CLASS {{p}}cx_ferry_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/files/reference/ferry_load_check.clas.abap b/tasks_gen/eval/G0127/files/reference/ferry_load_check.clas.abap new file mode 100644 index 0000000..ca35252 --- /dev/null +++ b/tasks_gen/eval/G0127/files/reference/ferry_load_check.clas.abap @@ -0,0 +1,92 @@ +CLASS {{p}}ferry_load_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_plate TYPE c LENGTH 10. + TYPES ty_category TYPE c LENGTH 1. + + TYPES: + BEGIN OF ty_vehicle, + plate TYPE ty_plate, + category TYPE ty_category, + weight_kg TYPE i, + length_cm TYPE i, + height_cm TYPE i, + hazard TYPE abap_bool, + END OF ty_vehicle. + + METHODS check_vehicle + IMPORTING is_vehicle TYPE ty_vehicle + RAISING {{p}}cx_ferry_error. + + METHODS check_loading + IMPORTING is_vehicle TYPE ty_vehicle + iv_free_weight TYPE i + iv_free_length TYPE i + iv_max_height TYPE i + RAISING {{p}}cx_ferry_error. + + PRIVATE SECTION. + METHODS raise_reason + IMPORTING iv_reason TYPE {{p}}if_ferry_reason=>ty_reason + RAISING {{p}}cx_ferry_error. + + METHODS is_plate_valid + IMPORTING iv_plate TYPE ty_plate + RETURNING VALUE(rv_valid) TYPE abap_bool. + + METHODS is_category_valid + IMPORTING iv_category TYPE ty_category + RETURNING VALUE(rv_valid) TYPE abap_bool. +ENDCLASS. + + +CLASS {{p}}ferry_load_check IMPLEMENTATION. + METHOD check_vehicle. + IF is_plate_valid( is_vehicle-plate ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-plate ). + ENDIF. + IF is_category_valid( is_vehicle-category ) = abap_false. + raise_reason( {{p}}if_ferry_reason=>c_reason-category ). + ENDIF. + IF is_vehicle-weight_kg <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight ). + ENDIF. + IF is_vehicle-length_cm <= 0 OR is_vehicle-height_cm <= 0. + raise_reason( {{p}}if_ferry_reason=>c_reason-size ). + ENDIF. + IF is_vehicle-hazard = abap_true AND is_vehicle-category <> 'T'. + raise_reason( {{p}}if_ferry_reason=>c_reason-hazard ). + ENDIF. + ENDMETHOD. + + METHOD check_loading. + check_vehicle( is_vehicle ). + IF is_vehicle-height_cm > iv_max_height. + raise_reason( {{p}}if_ferry_reason=>c_reason-height ). + ENDIF. + IF is_vehicle-weight_kg > iv_free_weight. + raise_reason( {{p}}if_ferry_reason=>c_reason-weight_limit ). + ENDIF. + IF is_vehicle-length_cm > iv_free_length. + raise_reason( {{p}}if_ferry_reason=>c_reason-length_limit ). + ENDIF. + ENDMETHOD. + + METHOD raise_reason. + RAISE EXCEPTION TYPE {{p}}cx_ferry_error + EXPORTING + iv_reason = iv_reason. + ENDMETHOD. + + METHOD is_plate_valid. + DATA lv_plate TYPE string. + lv_plate = to_upper( iv_plate ). + REPLACE ALL OCCURRENCES OF ` ` IN lv_plate WITH ``. + rv_valid = xsdbool( lv_plate IS NOT INITIAL + AND lv_plate CO 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-' ). + ENDMETHOD. + + METHOD is_category_valid. + rv_valid = xsdbool( iv_category = 'C' OR iv_category = 'V' + OR iv_category = 'T' OR iv_category = 'B' ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/files/reference/ferry_load_check.testclasses.abap b/tasks_gen/eval/G0127/files/reference/ferry_load_check.testclasses.abap new file mode 100644 index 0000000..e6406d5 --- /dev/null +++ b/tasks_gen/eval/G0127/files/reference/ferry_load_check.testclasses.abap @@ -0,0 +1,114 @@ +CLASS ltc_ferry_load_check DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}ferry_load_check. + DATA mo_error TYPE REF TO {{p}}cx_ferry_error. + METHODS setup. + METHODS valid_car_is_ok FOR TESTING. + METHODS empty_plate_rejected FOR TESTING. + METHODS hazard_in_car_rejected FOR TESTING. + METHODS weight_limit_rejected FOR TESTING. + METHODS plate_error_wins FOR TESTING. +ENDCLASS. + + +CLASS ltc_ferry_load_check IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}ferry_load_check( ). + ENDMETHOD. + + METHOD valid_car_is_ok. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>fail( msg = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD empty_plate_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = space + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'empty plate accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-plate + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD hazard_in_car_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 1500 + length_cm = 450 + height_cm = 150 + hazard = abap_true ). + TRY. + mo_cut->check_vehicle( is_vehicle = ls_vehicle ). + cl_abap_unit_assert=>fail( msg = 'hazard in car accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-hazard + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD weight_limit_rejected. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = 'B-AB 1234' + category = 'C' + weight_kg = 6000 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 5000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'weight limit accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-weight_limit + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. + + METHOD plate_error_wins. + DATA(ls_vehicle) = VALUE {{p}}ferry_load_check=>ty_vehicle( + plate = space + category = 'C' + weight_kg = 9999 + length_cm = 450 + height_cm = 150 + hazard = abap_false ). + TRY. + mo_cut->check_loading( is_vehicle = ls_vehicle + iv_free_weight = 1000 + iv_free_length = 2000 + iv_max_height = 200 ). + cl_abap_unit_assert=>fail( msg = 'empty plate accepted' ). + CATCH {{p}}cx_ferry_error INTO mo_error. + cl_abap_unit_assert=>assert_equals( + exp = {{p}}if_ferry_reason=>c_reason-plate + act = mo_error->reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0127/files/seed/if_ferry_reason.intf.abap b/tasks_gen/eval/G0127/files/seed/if_ferry_reason.intf.abap new file mode 100644 index 0000000..33e028d --- /dev/null +++ b/tasks_gen/eval/G0127/files/seed/if_ferry_reason.intf.abap @@ -0,0 +1,14 @@ +INTERFACE {{p}}if_ferry_reason PUBLIC. + TYPES ty_reason TYPE c LENGTH 12. + CONSTANTS: + BEGIN OF c_reason, + plate TYPE ty_reason VALUE 'PLATE', + category TYPE ty_reason VALUE 'CATEGORY', + weight TYPE ty_reason VALUE 'WEIGHT', + size TYPE ty_reason VALUE 'SIZE', + hazard TYPE ty_reason VALUE 'HAZARD', + height TYPE ty_reason VALUE 'HEIGHT', + weight_limit TYPE ty_reason VALUE 'WEIGHT_LIMIT', + length_limit TYPE ty_reason VALUE 'LENGTH_LIMIT', + END OF c_reason. +ENDINTERFACE. diff --git a/tasks_gen/eval/G0127/generation.json b/tasks_gen/eval/G0127/generation.json new file mode 100644 index 0000000..7b00888 --- /dev/null +++ b/tasks_gen/eval/G0127/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0127", + "pool": "eval", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "reference/ferry_load_check.clas.abap line 29: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"", + "reference/ferry_load_check.clas.abap line 33: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\"" + ], + "by_check": { + "abaplint": 2 + } + }, + { + "stage": "validate", + "oracle": 100.0, + "null": 0, + "mutation": { + "valid": 5, + "killed": 5, + "ok": true + } + } + ], + "accepted": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0127/mutation.json b/tasks_gen/eval/G0127/mutation.json new file mode 100644 index 0000000..677878a --- /dev/null +++ b/tasks_gen/eval/G0127/mutation.json @@ -0,0 +1,81 @@ +{ + "task": "G0127", + "mutants": [ + { + "object": "{{P}}FERRY_LOAD_CHECK", + "mutant": "line 44: abap_false -> abap_true (bool)", + "status": "killed", + "hidden": "0/12", + "failed_tests": [ + "BAD_PLATE_CHAR_REJECTED", + "EMPTY_PLATE_REJECTED", + "HAZARD_IN_CAR_REJECTED", + "LENGTH_LIMIT_REJECTED", + "PLATE_ERROR_WINS", + "TOO_HIGH_REJECTED", + "UNKNOWN_CATEGORY_REJECTED", + "VALID_CAR_IS_OK", + "VALID_TRUCK_HAZARD_OK", + "WEIGHT_LIMIT_REJECTED", + "ZERO_SIZE_REJECTED", + "ZERO_WEIGHT_REJECTED" + ] + }, + { + "object": "{{P}}FERRY_LOAD_CHECK", + "mutant": "line 47: = -> <> (eq)", + "status": "killed", + "hidden": "3/12", + "failed_tests": [ + "HAZARD_IN_CAR_REJECTED", + "LENGTH_LIMIT_REJECTED", + "TOO_HIGH_REJECTED", + "UNKNOWN_CATEGORY_REJECTED", + "VALID_CAR_IS_OK", + "VALID_TRUCK_HAZARD_OK", + "WEIGHT_LIMIT_REJECTED", + "ZERO_SIZE_REJECTED", + "ZERO_WEIGHT_REJECTED" + ] + }, + { + "object": "{{P}}FERRY_LOAD_CHECK", + "mutant": "line 50: <= -> > (rel)", + "status": "killed", + "hidden": "4/12", + "failed_tests": [ + "HAZARD_IN_CAR_REJECTED", + "LENGTH_LIMIT_REJECTED", + "TOO_HIGH_REJECTED", + "VALID_CAR_IS_OK", + "VALID_TRUCK_HAZARD_OK", + "WEIGHT_LIMIT_REJECTED", + "ZERO_SIZE_REJECTED", + "ZERO_WEIGHT_REJECTED" + ] + }, + { + "object": "{{P}}FERRY_LOAD_CHECK", + "mutant": "line 53: OR -> AND (logic)", + "status": "killed", + "hidden": "11/12", + "failed_tests": [ + "ZERO_SIZE_REJECTED" + ] + }, + { + "object": "{{P}}FERRY_LOAD_CHECK", + "mutant": "line 56: <> -> = (rel)", + "status": "killed", + "hidden": "10/12", + "failed_tests": [ + "HAZARD_IN_CAR_REJECTED", + "VALID_TRUCK_HAZARD_OK" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0127/spec.md b/tasks_gen/eval/G0127/spec.md new file mode 100644 index 0000000..7e956e4 --- /dev/null +++ b/tasks_gen/eval/G0127/spec.md @@ -0,0 +1,69 @@ +# 1. Goal +Check if a vehicle may board a ferry crossing. A loading planner calls the check +before it adds the vehicle to the loading list. The check reports the first reason +that stops the loading. + +# 2. Open questions +None. + +# 3. Context +- The interface {{P}}IF_FERRY_REASON exists in package $TMP. It is active. +- The interface has the type TY_REASON (CHAR 12) and the constant structure + C_REASON with the components PLATE, CATEGORY, WEIGHT, SIZE, HAZARD, HEIGHT, + WEIGHT_LIMIT and LENGTH_LIMIT. +- A vehicle has a plate, a category, a weight, a length, a height and a hazard + flag. + +# 4. Contract +- Create the class {{P}}CX_FERRY_ERROR in package $TMP. + - The class is public, final and inherits from CX_STATIC_CHECK. + - The class has the public read-only attribute REASON of type + {{P}}IF_FERRY_REASON=>TY_REASON. + - The class has a public constructor with the importing parameter IV_REASON of + type {{P}}IF_FERRY_REASON=>TY_REASON. The constructor stores IV_REASON in + REASON. + - Do not add other public components. +- Create the class {{P}}FERRY_LOAD_CHECK in package $TMP. + - The class is public, final and has a public constructor without parameters. + - The class has the public type TY_VEHICLE with the components PLATE (type c + length 10), CATEGORY (type c length 1), WEIGHT_KG (type i), LENGTH_CM (type + i), HEIGHT_CM (type i) and HAZARD (type abap_bool). + - The class has the public method CHECK_VEHICLE with the importing parameter + IS_VEHICLE of type TY_VEHICLE and the addition RAISING {{P}}CX_FERRY_ERROR. + - The class has the public method CHECK_LOADING with the importing parameters + IS_VEHICLE of type TY_VEHICLE, IV_FREE_WEIGHT of type i, IV_FREE_LENGTH of + type i and IV_MAX_HEIGHT of type i, and the addition RAISING + {{P}}CX_FERRY_ERROR. + - Do not add other public methods. + +# 5. Business rules +CHECK_VEHICLE applies the rules 1 to 5 in this sequence. CHECK_LOADING first calls +CHECK_VEHICLE and then applies the rules 6 to 8 in this sequence. The first rule +that fails raises the exception {{P}}CX_FERRY_ERROR with the reason of the rule. +1. Plate: remove all spaces from the plate and convert the letters to upper case. + If the result is empty or contains a character that is not a letter A to Z, a + digit 0 to 9 or a hyphen, the reason is PLATE. +2. Category: the category must be C, V, T or B. Else the reason is CATEGORY. +3. Weight: the weight must be greater than 0. Else the reason is WEIGHT. +4. Size: the length and the height must both be greater than 0. Else the reason + is SIZE. +5. Hazard: if the hazard flag is true and the category is not T, the reason is + HAZARD. +6. Height: if the height of the vehicle is greater than IV_MAX_HEIGHT, the reason + is HEIGHT. +7. Weight limit: if the weight of the vehicle is greater than IV_FREE_WEIGHT, the + reason is WEIGHT_LIMIT. +8. Length limit: if the length of the vehicle is greater than IV_FREE_LENGTH, the + reason is LENGTH_LIMIT. +If no rule fails, the method returns without an exception. + +# 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_FERRY_REASON. + +# 7. Acceptance +- The classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}FERRY_LOAD_CHECK. diff --git a/tasks_gen/eval/G0127/task.json b/tasks_gen/eval/G0127/task.json new file mode 100644 index 0000000..8220e32 --- /dev/null +++ b/tasks_gen/eval/G0127/task.json @@ -0,0 +1,60 @@ +{ + "id": "G0127", + "category": "D", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "INTF", + "name": "{{P}}IF_FERRY_REASON", + "file": "files/seed/if_ferry_reason.intf.abap", + "description": "Reason type and reason codes for the ferry loading check" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_FERRY_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}FERRY_LOAD_CHECK" + } + ], + "out_of_scope": [ + "{{P}}IF_FERRY_REASON" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "files/hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_FERRY_ERROR", + "file": "files/reference/cx_ferry_error.clas.abap", + "description": "Class-based exception that carries the reason" + }, + { + "type": "CLAS", + "name": "{{P}}FERRY_LOAD_CHECK", + "file": "files/reference/ferry_load_check.clas.abap", + "description": "Ferry loading check", + "testclasses_file": "files/reference/ferry_load_check.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "no_global_variables" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0128/generation.json b/tasks_gen/eval/G0128/generation.json new file mode 100644 index 0000000..0163d7b --- /dev/null +++ b/tasks_gen/eval/G0128/generation.json @@ -0,0 +1,29 @@ +{ + "id": "G0128", + "pool": "eval", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "validate", + "oracle": 0, + "null": 0 + }, + { + "stage": "validate", + "oracle": 92.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 92.5, + "null": 0 + }, + { + "stage": "validate", + "oracle": 92.5, + "null": 0 + } + ], + "accepted": false +} \ No newline at end of file diff --git a/tasks_gen/eval/G0128/hidden/crew_check_hidden.clas.abap b/tasks_gen/eval/G0128/hidden/crew_check_hidden.clas.abap new file mode 100644 index 0000000..a1abbde --- /dev/null +++ b/tasks_gen/eval/G0128/hidden/crew_check_hidden.clas.abap @@ -0,0 +1,172 @@ +CLASS {{p}}crew_check_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + DATA mo_cut TYPE REF TO {{p}}crew_duty_check. + METHODS setup. + METHODS base_duty + RETURNING VALUE(rs_duty) TYPE {{p}}crew_duty_check=>ty_duty. + METHODS valid_duty_passes FOR TESTING. + METHODS licence_invalid FOR TESTING. + METHODS rating_missing FOR TESTING. + METHODS rest_too_short FOR TESTING. + METHODS duty_too_long FOR TESTING. + METHODS month_limit FOR TESTING. + METHODS first_rule_wins FOR TESTING. + METHODS rating_before_rest FOR TESTING. + METHODS boundary_values FOR TESTING. + METHODS month_limit_exact FOR TESTING. +ENDCLASS. + + +CLASS {{p}}crew_check_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}crew_duty_check( ). + ENDMETHOD. + + METHOD base_duty. + CLEAR rs_duty. + rs_duty-crew_id = 'C0001'. + rs_duty-licence_valid = abap_true. + rs_duty-type_rating = 'A320'. + rs_duty-aircraft_type = 'A320'. + rs_duty-rest_hours = '12.0'. + rs_duty-duty_hours = '8.0'. + rs_duty-month_hours = '40.0'. + ENDMETHOD. + + METHOD valid_duty_passes. + DATA(ls_duty) = base_duty( ). + TRY. + mo_cut->check( ls_duty ). + CATCH {{p}}cx_licence_invalid + {{p}}cx_rating_missing + {{p}}cx_rest_too_short + {{p}}cx_duty_too_long + {{p}}cx_month_limit. + cl_abap_unit_assert=>fail( msg = 'valid duty was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD licence_invalid. + DATA(ls_duty) = base_duty( ). + ls_duty-licence_valid = abap_false. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'invalid licence was accepted' ). + CATCH {{p}}cx_licence_invalid. + ENDTRY. + ENDMETHOD. + + METHOD rating_missing. + DATA(ls_duty) = base_duty( ). + ls_duty-type_rating = 'B737'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'missing rating was accepted' ). + CATCH {{p}}cx_rating_missing INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = 'A320' + act = lo_exc->mv_required ). + cl_abap_unit_assert=>assert_equals( exp = 'B737' + act = lo_exc->mv_held ). + ENDTRY. + ENDMETHOD. + + METHOD rest_too_short. + DATA(ls_duty) = base_duty( ). + ls_duty-rest_hours = '9.9'. + DATA lv_expected TYPE ty_hours. + lv_expected = '9.9'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'short rest was accepted' ). + CATCH {{p}}cx_rest_too_short INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_rest_hours ). + ENDTRY. + ENDMETHOD. + + METHOD duty_too_long. + DATA(ls_duty) = base_duty( ). + ls_duty-duty_hours = '13.1'. + DATA lv_expected TYPE ty_hours. + lv_expected = '13.1'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'long duty was accepted' ). + CATCH {{p}}cx_duty_too_long INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_duty_hours ). + ENDTRY. + ENDMETHOD. + + METHOD month_limit. + DATA(ls_duty) = base_duty( ). + ls_duty-month_hours = '95.0'. + DATA lv_expected TYPE ty_hours. + lv_expected = '103.0'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'month limit was ignored' ). + CATCH {{p}}cx_month_limit INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_total_hours ). + ENDTRY. + ENDMETHOD. + + METHOD first_rule_wins. + DATA(ls_duty) = base_duty( ). + ls_duty-licence_valid = abap_false. + ls_duty-type_rating = 'B737'. + ls_duty-rest_hours = '2.0'. + ls_duty-duty_hours = '20.0'. + ls_duty-month_hours = '99.0'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'no rule matched' ). + CATCH {{p}}cx_licence_invalid. + ENDTRY. + ENDMETHOD. + + METHOD rating_before_rest. + DATA(ls_duty) = base_duty( ). + ls_duty-type_rating = 'B737'. + ls_duty-rest_hours = '2.0'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'no rule matched' ). + CATCH {{p}}cx_rating_missing. + ENDTRY. + ENDMETHOD. + + METHOD boundary_values. + DATA(ls_duty) = base_duty( ). + ls_duty-rest_hours = '10.0'. + ls_duty-duty_hours = '13.0'. + ls_duty-month_hours = '87.0'. + TRY. + mo_cut->check( ls_duty ). + CATCH {{p}}cx_licence_invalid + {{p}}cx_rating_missing + {{p}}cx_rest_too_short + {{p}}cx_duty_too_long + {{p}}cx_month_limit. + cl_abap_unit_assert=>fail( msg = 'boundary values were rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD month_limit_exact. + DATA(ls_duty) = base_duty( ). + ls_duty-duty_hours = '10.0'. + ls_duty-month_hours = '90.0'. + TRY. + mo_cut->check( ls_duty ). + CATCH {{p}}cx_licence_invalid + {{p}}cx_rating_missing + {{p}}cx_rest_too_short + {{p}}cx_duty_too_long + {{p}}cx_month_limit. + cl_abap_unit_assert=>fail( msg = 'exact monthly limit was rejected' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/mutation.json b/tasks_gen/eval/G0128/mutation.json new file mode 100644 index 0000000..a2df7bd --- /dev/null +++ b/tasks_gen/eval/G0128/mutation.json @@ -0,0 +1,71 @@ +{ + "task": "T30", + "mutants": [ + { + "object": "{{P}}CREW_DUTY_CHECK", + "mutant": "line 38: <> -> = (rel)", + "status": "killed", + "hidden": "10/10", + "failed_tests": [ + "VALID_DUTY_PASSES", + "LICENCE_INVALID", + "RATING_MISSING", + "REST_TOO_SHORT", + "DUTY_TOO_LONG", + "MONTH_LIMIT", + "FIRST_RULE_WINS", + "RATING_BEFORE_REST", + "BOUNDARY_VALUES", + "MONTH_LIMIT_EXACT" + ] + }, + { + "object": "{{P}}CREW_DUTY_CHECK", + "mutant": "line 42: <> -> = (rel)", + "status": "killed", + "hidden": "8/10", + "failed_tests": [ + "VALID_DUTY_PASSES", + "RATING_MISSING", + "REST_TOO_SHORT", + "DUTY_TOO_LONG", + "MONTH_LIMIT", + "RATING_BEFORE_REST", + "BOUNDARY_VALUES", + "MONTH_LIMIT_EXACT" + ] + }, + { + "object": "{{P}}CREW_DUTY_CHECK", + "mutant": "line 49: < -> <= (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "BOUNDARY_VALUES" + ] + }, + { + "object": "{{P}}CREW_DUTY_CHECK", + "mutant": "line 55: > -> >= (rel)", + "status": "killed", + "hidden": "1/10", + "failed_tests": [ + "BOUNDARY_VALUES" + ] + }, + { + "object": "{{P}}CREW_DUTY_CHECK", + "mutant": "line 63: > -> >= (rel)", + "status": "killed", + "hidden": "2/10", + "failed_tests": [ + "BOUNDARY_VALUES", + "MONTH_LIMIT_EXACT" + ] + } + ], + "valid": 5, + "killed": 5, + "kill_rate": 1.0, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0128/reference/crew_duty_check.clas.abap b/tasks_gen/eval/G0128/reference/crew_duty_check.clas.abap new file mode 100644 index 0000000..f33c0d7 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/crew_duty_check.clas.abap @@ -0,0 +1,69 @@ +CLASS {{p}}crew_duty_check DEFINITION + PUBLIC + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + TYPES: + BEGIN OF ty_duty, + crew_id TYPE c LENGTH 10, + licence_valid TYPE abap_bool, + type_rating TYPE c LENGTH 4, + aircraft_type TYPE c LENGTH 4, + rest_hours TYPE ty_hours, + duty_hours TYPE ty_hours, + month_hours TYPE ty_hours, + END OF ty_duty. + + METHODS check + IMPORTING is_duty TYPE ty_duty + RAISING + {{p}}cx_licence_invalid + {{p}}cx_rating_missing + {{p}}cx_rest_too_short + {{p}}cx_duty_too_long + {{p}}cx_month_limit. + + PRIVATE SECTION. + CONSTANTS: + c_min_rest_hours TYPE ty_hours VALUE '10.0', + c_max_duty_hours TYPE ty_hours VALUE '13.0', + c_max_month_hours TYPE ty_hours VALUE '100.0'. +ENDCLASS. + + +CLASS {{p}}crew_duty_check IMPLEMENTATION. + METHOD check. + IF is_duty-licence_valid <> abap_true. + RAISE EXCEPTION TYPE {{p}}cx_licence_invalid. + ENDIF. + + IF is_duty-type_rating <> is_duty-aircraft_type. + RAISE EXCEPTION TYPE {{p}}cx_rating_missing + EXPORTING + iv_required = is_duty-aircraft_type + iv_held = is_duty-type_rating. + ENDIF. + + IF is_duty-rest_hours < c_min_rest_hours. + RAISE EXCEPTION TYPE {{p}}cx_rest_too_short + EXPORTING + iv_rest_hours = is_duty-rest_hours. + ENDIF. + + IF is_duty-duty_hours > c_max_duty_hours. + RAISE EXCEPTION TYPE {{p}}cx_duty_too_long + EXPORTING + iv_duty_hours = is_duty-duty_hours. + ENDIF. + + DATA lv_total_hours TYPE ty_hours. + lv_total_hours = is_duty-month_hours + is_duty-duty_hours. + IF lv_total_hours > c_max_month_hours. + RAISE EXCEPTION TYPE {{p}}cx_month_limit + EXPORTING + iv_total_hours = lv_total_hours. + ENDIF. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/crew_duty_check.testclasses.abap b/tasks_gen/eval/G0128/reference/crew_duty_check.testclasses.abap new file mode 100644 index 0000000..d8af0c4 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/crew_duty_check.testclasses.abap @@ -0,0 +1,151 @@ +CLASS ltc_crew_duty_check DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + DATA mo_cut TYPE REF TO {{p}}crew_duty_check. + METHODS setup. + METHODS base_duty + RETURNING VALUE(rs_duty) TYPE {{p}}crew_duty_check=>ty_duty. + METHODS valid_duty_is_ok FOR TESTING. + METHODS invalid_licence FOR TESTING. + METHODS missing_rating FOR TESTING. + METHODS rest_below_minimum FOR TESTING. + METHODS rest_at_minimum FOR TESTING. + METHODS duty_above_maximum FOR TESTING. + METHODS duty_at_maximum FOR TESTING. + METHODS month_limit_exceeded FOR TESTING. + METHODS month_limit_exact FOR TESTING. +ENDCLASS. + + +CLASS ltc_crew_duty_check IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}crew_duty_check( ). + ENDMETHOD. + + METHOD base_duty. + CLEAR rs_duty. + rs_duty-crew_id = 'C0001'. + rs_duty-licence_valid = abap_true. + rs_duty-type_rating = 'A320'. + rs_duty-aircraft_type = 'A320'. + rs_duty-rest_hours = '12.0'. + rs_duty-duty_hours = '8.0'. + rs_duty-month_hours = '40.0'. + ENDMETHOD. + + METHOD valid_duty_is_ok. + DATA(ls_duty) = base_duty( ). + TRY. + mo_cut->check( ls_duty ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'valid duty was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD invalid_licence. + DATA(ls_duty) = base_duty( ). + ls_duty-licence_valid = abap_false. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'invalid licence was accepted' ). + CATCH {{p}}cx_licence_invalid. + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD missing_rating. + DATA(ls_duty) = base_duty( ). + ls_duty-type_rating = 'B737'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'missing rating was accepted' ). + CATCH {{p}}cx_rating_missing INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = 'A320' + act = lo_exc->mv_required ). + cl_abap_unit_assert=>assert_equals( exp = 'B737' + act = lo_exc->mv_held ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD rest_below_minimum. + DATA(ls_duty) = base_duty( ). + ls_duty-rest_hours = '9.9'. + DATA lv_expected TYPE ty_hours. + lv_expected = '9.9'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'short rest was accepted' ). + CATCH {{p}}cx_rest_too_short INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_rest_hours ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD rest_at_minimum. + DATA(ls_duty) = base_duty( ). + ls_duty-rest_hours = '10.0'. + TRY. + mo_cut->check( ls_duty ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'minimum rest was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD duty_above_maximum. + DATA(ls_duty) = base_duty( ). + ls_duty-duty_hours = '13.1'. + DATA lv_expected TYPE ty_hours. + lv_expected = '13.1'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'long duty was accepted' ). + CATCH {{p}}cx_duty_too_long INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_duty_hours ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD duty_at_maximum. + DATA(ls_duty) = base_duty( ). + ls_duty-duty_hours = '13.0'. + TRY. + mo_cut->check( ls_duty ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'maximum duty was rejected' ). + ENDTRY. + ENDMETHOD. + + METHOD month_limit_exceeded. + DATA(ls_duty) = base_duty( ). + ls_duty-month_hours = '95.0'. + DATA lv_expected TYPE ty_hours. + lv_expected = '103.0'. + TRY. + mo_cut->check( ls_duty ). + cl_abap_unit_assert=>fail( msg = 'month limit was ignored' ). + CATCH {{p}}cx_month_limit INTO DATA(lo_exc). + cl_abap_unit_assert=>assert_equals( exp = lv_expected + act = lo_exc->mv_total_hours ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'wrong exception type' ). + ENDTRY. + ENDMETHOD. + + METHOD month_limit_exact. + DATA(ls_duty) = base_duty( ). + ls_duty-duty_hours = '8.0'. + ls_duty-month_hours = '92.0'. + TRY. + mo_cut->check( ls_duty ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'exact monthly limit was rejected' ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/cx_duty_too_long.clas.abap b/tasks_gen/eval/G0128/reference/cx_duty_too_long.clas.abap new file mode 100644 index 0000000..3ba7ea8 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/cx_duty_too_long.clas.abap @@ -0,0 +1,23 @@ +CLASS {{p}}cx_duty_too_long DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + + DATA mv_duty_hours TYPE ty_hours READ-ONLY. + + METHODS constructor + IMPORTING + iv_duty_hours TYPE ty_hours. +ENDCLASS. + + +CLASS {{p}}cx_duty_too_long IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_duty_hours = iv_duty_hours. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/cx_licence_invalid.clas.abap b/tasks_gen/eval/G0128/reference/cx_licence_invalid.clas.abap new file mode 100644 index 0000000..e7b49ef --- /dev/null +++ b/tasks_gen/eval/G0128/reference/cx_licence_invalid.clas.abap @@ -0,0 +1,12 @@ +CLASS {{p}}cx_licence_invalid DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. +ENDCLASS. + + +CLASS {{p}}cx_licence_invalid IMPLEMENTATION. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/cx_month_limit.clas.abap b/tasks_gen/eval/G0128/reference/cx_month_limit.clas.abap new file mode 100644 index 0000000..e69d083 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/cx_month_limit.clas.abap @@ -0,0 +1,23 @@ +CLASS {{p}}cx_month_limit DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + + DATA mv_total_hours TYPE ty_hours READ-ONLY. + + METHODS constructor + IMPORTING + iv_total_hours TYPE ty_hours. +ENDCLASS. + + +CLASS {{p}}cx_month_limit IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_total_hours = iv_total_hours. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/cx_rating_missing.clas.abap b/tasks_gen/eval/G0128/reference/cx_rating_missing.clas.abap new file mode 100644 index 0000000..5556f43 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/cx_rating_missing.clas.abap @@ -0,0 +1,26 @@ +CLASS {{p}}cx_rating_missing DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_rating TYPE c LENGTH 4. + + DATA mv_required TYPE ty_rating READ-ONLY. + DATA mv_held TYPE ty_rating READ-ONLY. + + METHODS constructor + IMPORTING + iv_required TYPE ty_rating + iv_held TYPE ty_rating. +ENDCLASS. + + +CLASS {{p}}cx_rating_missing IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_required = iv_required. + mv_held = iv_held. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/reference/cx_rest_too_short.clas.abap b/tasks_gen/eval/G0128/reference/cx_rest_too_short.clas.abap new file mode 100644 index 0000000..4567d52 --- /dev/null +++ b/tasks_gen/eval/G0128/reference/cx_rest_too_short.clas.abap @@ -0,0 +1,23 @@ +CLASS {{p}}cx_rest_too_short DEFINITION + PUBLIC + INHERITING FROM cx_static_check + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_hours TYPE p LENGTH 4 DECIMALS 1. + + DATA mv_rest_hours TYPE ty_hours READ-ONLY. + + METHODS constructor + IMPORTING + iv_rest_hours TYPE ty_hours. +ENDCLASS. + + +CLASS {{p}}cx_rest_too_short IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_rest_hours = iv_rest_hours. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0128/spec.md b/tasks_gen/eval/G0128/spec.md new file mode 100644 index 0000000..084e508 --- /dev/null +++ b/tasks_gen/eval/G0128/spec.md @@ -0,0 +1,100 @@ +# 1. Goal + +A rostering system assigns pilots to flight duties. Before the system confirms an +assignment, it calls one check. The check rejects the assignment with a reason if +the pilot may not fly the duty. The system needs one exception for each reason. + +# 2. Open questions +None. + +# 3. Context + +- All objects are in package $TMP. +- The check uses only the data that the caller passes. It does not read a database. +- The caller fills the structure TY_DUTY and calls the method CHECK. + +# 4. Contract + +## 4.1 Class {{P}}CREW_DUTY_CHECK + +- Package $TMP. The class is public, final and create public. +- The class can be created without parameters. +- Public type TY_HOURS: packed number, length 4, 1 decimal place. +- Public type TY_DUTY, a structure with these components: + + | Component | Type | + | --- | --- | + | CREW_ID | c LENGTH 10 | + | LICENCE_VALID | abap_bool | + | TYPE_RATING | c LENGTH 4 | + | AIRCRAFT_TYPE | c LENGTH 4 | + | REST_HOURS | ty_hours | + | DUTY_HOURS | ty_hours | + | MONTH_HOURS | ty_hours | + +- Public method CHECK: + - IMPORTING is_duty TYPE ty_duty + - RAISING {{P}}CX_LICENCE_INVALID, {{P}}CX_RATING_MISSING, + {{P}}CX_REST_TOO_SHORT, {{P}}CX_DUTY_TOO_LONG, {{P}}CX_MONTH_LIMIT + - The method has no return value. + +## 4.2 Exception classes + +Create these five classes in package $TMP. Each class is public, final, create public +and inherits from CX_STATIC_CHECK. + +{{P}}CX_LICENCE_INVALID +- No own components. + +{{P}}CX_RATING_MISSING +- Public type TY_RATING: character, length 4. +- Public read-only attributes MV_REQUIRED and MV_HELD, type TY_RATING. +- Public constructor with importing parameters IV_REQUIRED and IV_HELD, type TY_RATING. + The constructor copies IV_REQUIRED to MV_REQUIRED and IV_HELD to MV_HELD. + +{{P}}CX_REST_TOO_SHORT +- Public type TY_HOURS: packed number, length 4, 1 decimal place. +- Public read-only attribute MV_REST_HOURS, type TY_HOURS. +- Public constructor with importing parameter IV_REST_HOURS, type TY_HOURS. + The constructor copies IV_REST_HOURS to MV_REST_HOURS. + +{{P}}CX_DUTY_TOO_LONG +- Public type TY_HOURS: packed number, length 4, 1 decimal place. +- Public read-only attribute MV_DUTY_HOURS, type TY_HOURS. +- Public constructor with importing parameter IV_DUTY_HOURS, type TY_HOURS. + The constructor copies IV_DUTY_HOURS to MV_DUTY_HOURS. + +{{P}}CX_MONTH_LIMIT +- Public type TY_HOURS: packed number, length 4, 1 decimal place. +- Public read-only attribute MV_TOTAL_HOURS, type TY_HOURS. +- Public constructor with importing parameter IV_TOTAL_HOURS, type TY_HOURS. + The constructor copies IV_TOTAL_HOURS to MV_TOTAL_HOURS. + +# 5. Business rules + +The method CHECK applies these rules in this sequence. It raises the exception of the +first rule that fails and stops. If no rule fails, the method returns to the caller. + +1. Licence. If is_duty-licence_valid is not abap_true, raise {{P}}CX_LICENCE_INVALID. +2. Type rating. If is_duty-type_rating is not equal to is_duty-aircraft_type, raise + {{P}}CX_RATING_MISSING. Pass is_duty-aircraft_type as IV_REQUIRED and + is_duty-type_rating as IV_HELD. +3. Rest. If is_duty-rest_hours is less than 10.0, raise {{P}}CX_REST_TOO_SHORT. + Pass is_duty-rest_hours as IV_REST_HOURS. +4. Duty period. If is_duty-duty_hours is greater than 13.0, raise + {{P}}CX_DUTY_TOO_LONG. Pass is_duty-duty_hours as IV_DUTY_HOURS. +5. Monthly hours. Add is_duty-month_hours and is_duty-duty_hours. If the sum is + greater than 100.0, raise {{P}}CX_MONTH_LIMIT. Pass the sum as IV_TOTAL_HOURS. + +# 6. Constraints + +- Release target: SAP_BASIS 8.16. +- Coding standards: Clean ABAP. Methods below 40 statements. No global variables. + No comment that restates the code. +- Out of scope: database tables, message classes, reports, function modules, dynpros. + +# 7. Acceptance + +- The six classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for the class {{P}}CREW_DUTY_CHECK. diff --git a/tasks_gen/eval/G0128/task.json b/tasks_gen/eval/G0128/task.json new file mode 100644 index 0000000..fc107e1 --- /dev/null +++ b/tasks_gen/eval/G0128/task.json @@ -0,0 +1,97 @@ +{ + "id": "G0128", + "category": "D", + "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}}CREW_DUTY_CHECK" + }, + { + "type": "CLAS", + "name": "{{P}}CX_LICENCE_INVALID" + }, + { + "type": "CLAS", + "name": "{{P}}CX_RATING_MISSING" + }, + { + "type": "CLAS", + "name": "{{P}}CX_REST_TOO_SHORT" + }, + { + "type": "CLAS", + "name": "{{P}}CX_DUTY_TOO_LONG" + }, + { + "type": "CLAS", + "name": "{{P}}CX_MONTH_LIMIT" + } + ], + "out_of_scope": [ + "Database tables", + "Message classes", + "Reports", + "Function modules", + "Dynpros" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}CREW_CHECK_HIDDEN", + "file": "hidden/crew_check_hidden.clas.abap", + "description": "Hidden tests for the crew duty check and its exception classes" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_LICENCE_INVALID", + "file": "reference/cx_licence_invalid.clas.abap", + "description": "Exception: licence is not valid" + }, + { + "type": "CLAS", + "name": "{{P}}CX_RATING_MISSING", + "file": "reference/cx_rating_missing.clas.abap", + "description": "Exception: type rating does not match the aircraft type" + }, + { + "type": "CLAS", + "name": "{{P}}CX_REST_TOO_SHORT", + "file": "reference/cx_rest_too_short.clas.abap", + "description": "Exception: rest period is too short" + }, + { + "type": "CLAS", + "name": "{{P}}CX_DUTY_TOO_LONG", + "file": "reference/cx_duty_too_long.clas.abap", + "description": "Exception: flight duty period is too long" + }, + { + "type": "CLAS", + "name": "{{P}}CX_MONTH_LIMIT", + "file": "reference/cx_month_limit.clas.abap", + "description": "Exception: monthly hour limit is exceeded" + }, + { + "type": "CLAS", + "name": "{{P}}CREW_DUTY_CHECK", + "file": "reference/crew_duty_check.clas.abap", + "description": "Crew duty check", + "testclasses_file": "reference/crew_duty_check.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "cyclomatic_complexity" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0129/generation.json b/tasks_gen/eval/G0129/generation.json new file mode 100644 index 0000000..cae9b81 --- /dev/null +++ b/tasks_gen/eval/G0129/generation.json @@ -0,0 +1,69 @@ +{ + "id": "G0129", + "pool": "eval", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "hidden/t_print_hidden.clas.abap line 72: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 72: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 77: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 82: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 84: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 84: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 89: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 89: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "hidden/t_print_hidden.clas.abap line 91: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 91: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"75\"", + "hidden/t_print_hidden.clas.abap line 96: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 96: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 101: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 101: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 106: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 106: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "hidden/t_print_hidden.clas.abap line 144: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"assert_price\"", + "hidden/t_print_hidden.clas.abap line 144: 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": "bundle", + "errors": [ + "reference/print_job_pricer.testclasses.abap line 42: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/print_job_pricer.testclasses.abap line 43: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "reference/print_job_pricer.testclasses.abap line 49: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/print_job_pricer.testclasses.abap line 50: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "reference/print_job_pricer.testclasses.abap line 56: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/print_job_pricer.testclasses.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"50\"", + "reference/print_job_pricer.testclasses.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"cl_abap_unit_assert\"", + "reference/print_job_pricer.testclasses.abap line 64: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "reference/print_job_pricer.clas.abap line 26: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"00\"", + "reference/print_job_pricer.clas.abap line 28: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"05\"", + "reference/print_job_pricer.clas.abap line 29: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"10\"", + "reference/print_job_pricer.clas.abap line 123: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"03\"", + "reference/print_job_pricer.clas.abap line 125: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"05\"", + "reference/print_job_pricer.clas.abap line 127: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"09\"" + ], + "by_check": { + "abaplint": 14 + } + }, + { + "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/eval/G0129/hidden/t_print_hidden.clas.abap b/tasks_gen/eval/G0129/hidden/t_print_hidden.clas.abap new file mode 100644 index 0000000..436c5d1 --- /dev/null +++ b/tasks_gen/eval/G0129/hidden/t_print_hidden.clas.abap @@ -0,0 +1,267 @@ +CLASS {{p}}t_print_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + METHODS a4_mono_price FOR TESTING. + METHODS a4_color_price FOR TESTING. + METHODS a3_a5_price FOR TESTING. + METHODS volume_discount_5 FOR TESTING. + METHODS volume_discount_10 FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS copies_multiply FOR TESTING. + METHODS rejects_zero_pages FOR TESTING. + METHODS rejects_zero_copies FOR TESTING. + METHODS rejects_unknown_values FOR TESTING. + METHODS rejects_over_limit FOR TESTING. + METHODS first_error_wins FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t_print_hidden IMPLEMENTATION. + METHOD a4_mono_price. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 500 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD a4_color_price. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'C' + iv_dept = 'SALE' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 2000 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD a3_a5_price. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A3' + iv_color = 'M' + iv_dept = 'PROD' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 900 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + TRY. + DATA(lv_a5) = lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A5' + iv_color = 'M' + iv_dept = 'HR' ). + lv_cents = lv_a5 * 100. + cl_abap_unit_assert=>assert_equals( exp = 300 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD volume_discount_5. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 600 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'IT' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 2850 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + TRY. + DATA(lv_500) = lo_cut->calculate_price( iv_pages = 500 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'IT' ). + lv_cents = lv_500 * 100. + cl_abap_unit_assert=>assert_equals( exp = 2375 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD volume_discount_10. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 1000 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 4500 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD minimum_charge. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 10 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 100 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD copies_multiply. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 200 + iv_copies = 2 + iv_paper = 'A3' + iv_color = 'C' + iv_dept = 'PROD' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 14400 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_zero_pages. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + lo_cut->calculate_price( iv_pages = 0 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-pages + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_zero_copies. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + lo_cut->calculate_price( iv_pages = 100 + iv_copies = 0 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-copies + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_unknown_values. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'B5' + iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_paper). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-paper + act = lo_paper->mv_reason ). + ENDTRY. + TRY. + lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'X' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_color). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-color + act = lo_color->mv_reason ). + ENDTRY. + TRY. + lo_cut->calculate_price( iv_pages = 100 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'XYZ' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_dept). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-dept + act = lo_dept->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD rejects_over_limit. + DATA lv_cents TYPE i. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + lo_cut->calculate_price( iv_pages = 3000 + iv_copies = 2 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_limit INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 6000 + act = lo_error->mv_pages ). + ENDTRY. + TRY. + DATA(lv_price) = lo_cut->calculate_price( iv_pages = 5000 + iv_copies = 1 + iv_paper = 'A4' + iv_color = 'M' + iv_dept = 'SALE' ). + lv_cents = lv_price * 100. + cl_abap_unit_assert=>assert_equals( exp = 22500 act = lv_cents ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD first_error_wins. + DATA(lo_cut) = NEW {{p}}print_job_pricer( ). + TRY. + lo_cut->calculate_price( iv_pages = 0 + iv_copies = 0 + iv_paper = 'B5' + iv_color = 'X' + iv_dept = 'XYZ' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-pages + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0129/reference/cx_print_job_error.clas.abap b/tasks_gen/eval/G0129/reference/cx_print_job_error.clas.abap new file mode 100644 index 0000000..5635b72 --- /dev/null +++ b/tasks_gen/eval/G0129/reference/cx_print_job_error.clas.abap @@ -0,0 +1,31 @@ +CLASS {{p}}cx_print_job_error DEFINITION + PUBLIC + INHERITING FROM CX_ROOT + FINAL + CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 10. + + CONSTANTS: + BEGIN OF c_reason, + pages TYPE ty_reason VALUE 'PAGES', + copies TYPE ty_reason VALUE 'COPIES', + paper TYPE ty_reason VALUE 'PAPER', + color TYPE ty_reason VALUE 'COLOR', + dept TYPE ty_reason VALUE 'DEPT', + END OF c_reason. + + DATA mv_reason TYPE ty_reason. + + METHODS constructor + IMPORTING + iv_reason TYPE ty_reason. +ENDCLASS. + + +CLASS {{p}}cx_print_job_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0129/reference/cx_print_limit.clas.abap b/tasks_gen/eval/G0129/reference/cx_print_limit.clas.abap new file mode 100644 index 0000000..1bad0ce --- /dev/null +++ b/tasks_gen/eval/G0129/reference/cx_print_limit.clas.abap @@ -0,0 +1,20 @@ +CLASS {{p}}cx_print_limit DEFINITION + PUBLIC + INHERITING FROM CX_ROOT + FINAL + CREATE PUBLIC. + PUBLIC SECTION. + DATA mv_pages TYPE i. + + METHODS constructor + IMPORTING + iv_pages TYPE i. +ENDCLASS. + + +CLASS {{p}}cx_print_limit IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_pages = iv_pages. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0129/reference/print_job_pricer.clas.abap b/tasks_gen/eval/G0129/reference/print_job_pricer.clas.abap new file mode 100644 index 0000000..79aa0ac --- /dev/null +++ b/tasks_gen/eval/G0129/reference/print_job_pricer.clas.abap @@ -0,0 +1,158 @@ +CLASS {{p}}print_job_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_paper TYPE c LENGTH 4, + ty_color TYPE c LENGTH 1, + ty_dept TYPE c LENGTH 4, + ty_price TYPE p LENGTH 8 DECIMALS 2. + + CONSTANTS: + BEGIN OF c_paper, + a3 TYPE ty_paper VALUE 'A3', + a4 TYPE ty_paper VALUE 'A4', + a5 TYPE ty_paper VALUE 'A5', + END OF c_paper, + BEGIN OF c_color, + mono TYPE ty_color VALUE 'M', + color TYPE ty_color VALUE 'C', + END OF c_color, + BEGIN OF c_dept, + sale TYPE ty_dept VALUE 'SALE', + hr TYPE ty_dept VALUE 'HR', + it TYPE ty_dept VALUE 'IT', + prod TYPE ty_dept VALUE 'PROD', + END OF c_dept, + c_max_pages TYPE i VALUE 5000, + c_min_cents TYPE i VALUE 100, + c_cents_a5 TYPE i VALUE 3, + c_cents_a4 TYPE i VALUE 5, + c_cents_a3 TYPE i VALUE 9, + c_color_factor TYPE i VALUE 4, + c_cents_per_unit TYPE i VALUE 100, + c_percent_base TYPE i VALUE 100, + c_percent_5 TYPE i VALUE 5, + c_percent_10 TYPE i VALUE 10, + c_pages_discount_5 TYPE i VALUE 500, + c_pages_discount_10 TYPE i VALUE 1000. + + METHODS calculate_price + IMPORTING + iv_pages TYPE i + iv_copies TYPE i + iv_paper TYPE ty_paper + iv_color TYPE ty_color + iv_dept TYPE ty_dept + RETURNING + VALUE(rv_price) TYPE ty_price + RAISING + {{p}}cx_print_job_error + {{p}}cx_print_limit. + + PRIVATE SECTION. + METHODS validate + IMPORTING + iv_pages TYPE i + iv_copies TYPE i + iv_paper TYPE ty_paper + iv_color TYPE ty_color + iv_dept TYPE ty_dept + RAISING + {{p}}cx_print_job_error. + METHODS page_price_cents + IMPORTING + iv_paper TYPE ty_paper + iv_color TYPE ty_color + RETURNING + VALUE(rv_cents) TYPE i. + METHODS discount_cents + IMPORTING + iv_total_pages TYPE i + iv_gross_cents TYPE i + RETURNING + VALUE(rv_cents) TYPE i. +ENDCLASS. + + +CLASS {{p}}print_job_pricer IMPLEMENTATION. + METHOD calculate_price. + validate( iv_pages = iv_pages + iv_copies = iv_copies + iv_paper = iv_paper + iv_color = iv_color + iv_dept = iv_dept ). + + DATA(lv_total_pages) = iv_pages * iv_copies. + IF lv_total_pages > c_max_pages. + RAISE EXCEPTION TYPE {{p}}cx_print_limit + EXPORTING + iv_pages = lv_total_pages. + ENDIF. + + DATA(lv_gross_cents) = lv_total_pages * page_price_cents( + iv_paper = iv_paper + iv_color = iv_color ). + DATA(lv_net_cents) = lv_gross_cents - discount_cents( + iv_total_pages = lv_total_pages + iv_gross_cents = lv_gross_cents ). + IF lv_net_cents < c_min_cents. + lv_net_cents = c_min_cents. + ENDIF. + + DATA(lv_net) = CONV ty_price( lv_net_cents ). + rv_price = lv_net / c_cents_per_unit. + ENDMETHOD. + + METHOD validate. + IF iv_pages < 1. + RAISE EXCEPTION TYPE {{p}}cx_print_job_error + EXPORTING iv_reason = {{p}}cx_print_job_error=>c_reason-pages. + ENDIF. + IF iv_copies < 1. + RAISE EXCEPTION TYPE {{p}}cx_print_job_error + EXPORTING iv_reason = {{p}}cx_print_job_error=>c_reason-copies. + ENDIF. + IF iv_paper <> c_paper-a4 AND iv_paper <> c_paper-a3 + AND iv_paper <> c_paper-a5. + RAISE EXCEPTION TYPE {{p}}cx_print_job_error + EXPORTING iv_reason = {{p}}cx_print_job_error=>c_reason-paper. + ENDIF. + IF iv_color <> c_color-color AND iv_color <> c_color-mono. + RAISE EXCEPTION TYPE {{p}}cx_print_job_error + EXPORTING iv_reason = {{p}}cx_print_job_error=>c_reason-color. + ENDIF. + IF iv_dept <> c_dept-sale AND iv_dept <> c_dept-hr + AND iv_dept <> c_dept-it AND iv_dept <> c_dept-prod. + RAISE EXCEPTION TYPE {{p}}cx_print_job_error + EXPORTING iv_reason = {{p}}cx_print_job_error=>c_reason-dept. + ENDIF. + ENDMETHOD. + + METHOD page_price_cents. + CASE iv_paper. + WHEN c_paper-a5. + rv_cents = c_cents_a5. + WHEN c_paper-a4. + rv_cents = c_cents_a4. + WHEN c_paper-a3. + rv_cents = c_cents_a3. + ENDCASE. + IF iv_color = c_color-color. + rv_cents = rv_cents * c_color_factor. + ENDIF. + ENDMETHOD. + + METHOD discount_cents. + DATA lv_percent TYPE i. + IF iv_total_pages >= c_pages_discount_10. + lv_percent = c_percent_10. + ELSEIF iv_total_pages >= c_pages_discount_5. + lv_percent = c_percent_5. + ELSE. + RETURN. + ENDIF. + DATA lv_gross TYPE ty_price. + lv_gross = iv_gross_cents. + DATA(lv_discount) = lv_gross * lv_percent / c_percent_base. + rv_cents = lv_discount. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0129/reference/print_job_pricer.testclasses.abap b/tasks_gen/eval/G0129/reference/print_job_pricer.testclasses.abap new file mode 100644 index 0000000..e146e69 --- /dev/null +++ b/tasks_gen/eval/G0129/reference/print_job_pricer.testclasses.abap @@ -0,0 +1,104 @@ +CLASS ltc_print_job_pricer DEFINITION FINAL FOR TESTING + DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}print_job_pricer. + METHODS setup. + METHODS price + IMPORTING + iv_pages TYPE i + iv_copies TYPE i + iv_paper TYPE {{p}}print_job_pricer=>ty_paper + iv_color TYPE {{p}}print_job_pricer=>ty_color + iv_dept TYPE {{p}}print_job_pricer=>ty_dept + RETURNING + VALUE(rv_price) TYPE {{p}}print_job_pricer=>ty_price. + METHODS cents + IMPORTING + iv_cents TYPE i + RETURNING + VALUE(rv_price) TYPE {{p}}print_job_pricer=>ty_price. + METHODS a4_mono_price FOR TESTING. + METHODS color_price FOR TESTING. + METHODS volume_discount FOR TESTING. + METHODS minimum_charge FOR TESTING. + METHODS zero_pages_raises FOR TESTING. + METHODS over_limit_raises FOR TESTING. +ENDCLASS. + + +CLASS ltc_print_job_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}print_job_pricer( ). + ENDMETHOD. + + METHOD price. + TRY. + rv_price = mo_cut->calculate_price( iv_pages = iv_pages + iv_copies = iv_copies + iv_paper = iv_paper + iv_color = iv_color + iv_dept = iv_dept ). + CATCH cx_root. + cl_abap_unit_assert=>fail( msg = 'unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD cents. + DATA lv_price TYPE {{p}}print_job_pricer=>ty_price. + lv_price = iv_cents. + rv_price = lv_price / 100. + ENDMETHOD. + + METHOD a4_mono_price. + cl_abap_unit_assert=>assert_equals( + exp = cents( 500 ) + act = price( iv_pages = 100 iv_copies = 1 + iv_paper = 'A4' iv_color = 'M' iv_dept = 'SALE' ) ). + ENDMETHOD. + + METHOD color_price. + cl_abap_unit_assert=>assert_equals( + exp = cents( 2000 ) + act = price( iv_pages = 100 iv_copies = 1 + iv_paper = 'A4' iv_color = 'C' iv_dept = 'SALE' ) ). + ENDMETHOD. + + METHOD volume_discount. + cl_abap_unit_assert=>assert_equals( + exp = cents( 2850 ) + act = price( iv_pages = 600 iv_copies = 1 + iv_paper = 'A4' iv_color = 'M' iv_dept = 'IT' ) ). + ENDMETHOD. + + METHOD minimum_charge. + cl_abap_unit_assert=>assert_equals( + exp = cents( 100 ) + act = price( iv_pages = 10 iv_copies = 1 + iv_paper = 'A4' iv_color = 'M' iv_dept = 'SALE' ) ). + ENDMETHOD. + + METHOD zero_pages_raises. + TRY. + mo_cut->calculate_price( iv_pages = 0 iv_copies = 1 + iv_paper = 'A4' iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_job_error INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( + exp = {{p}}cx_print_job_error=>c_reason-pages + act = lo_error->mv_reason ). + ENDTRY. + ENDMETHOD. + + METHOD over_limit_raises. + TRY. + mo_cut->calculate_price( iv_pages = 3000 iv_copies = 2 + iv_paper = 'A4' iv_color = 'M' + iv_dept = 'SALE' ). + cl_abap_unit_assert=>fail( msg = 'exception expected' ). + CATCH {{p}}cx_print_limit INTO DATA(lo_error). + cl_abap_unit_assert=>assert_equals( exp = 6000 + act = lo_error->mv_pages ). + ENDTRY. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0129/spec.md b/tasks_gen/eval/G0129/spec.md new file mode 100644 index 0000000..2bed35c --- /dev/null +++ b/tasks_gen/eval/G0129/spec.md @@ -0,0 +1,85 @@ +# 1. Goal +Calculate the internal price of a print job for the print shop of a company. A +billing program calls the calculation before it charges the cost centre of a +department. Invalid job data must raise a class-based exception. + +# 2. Open questions +None. + +# 3. Context +- Package $TMP. No other object exists for this topic. +- The billing program catches the exceptions and shows the reason to the user. + The names of the classes and their public components are fixed. + +# 4. Contract +Create three classes in package $TMP. + +## {{P}}CX_PRINT_JOB_ERROR +- Public, final, inherits from CX_ROOT. +- Public type TY_REASON, character 10. +- Public constant structure C_REASON with the components PAGES, COPIES, PAPER, + COLOR and DEPT. The values are 'PAGES', 'COPIES', 'PAPER', 'COLOR' and + 'DEPT'. +- Public attribute MV_REASON of type TY_REASON. +- Public constructor with the importing parameter IV_REASON of type TY_REASON. + The constructor writes IV_REASON to MV_REASON. + +## {{P}}CX_PRINT_LIMIT +- Public, final, inherits from CX_ROOT. +- Public attribute MV_PAGES of type I. +- Public constructor with the importing parameter IV_PAGES of type I. The + constructor writes IV_PAGES to MV_PAGES. + +## {{P}}PRINT_JOB_PRICER +- Public, final, public constructor without parameters. +- Public types: TY_PAPER (character 4), TY_COLOR (character 1), TY_DEPT + (character 4), TY_PRICE (packed number, length 8, 2 decimals). +- Public method CALCULATE_PRICE with these parameters: + - IMPORTING IV_PAGES TYPE I + - IMPORTING IV_COPIES TYPE I + - IMPORTING IV_PAPER TYPE TY_PAPER + - IMPORTING IV_COLOR TYPE TY_COLOR + - IMPORTING IV_DEPT TYPE TY_DEPT + - RETURNING VALUE(RV_PRICE) TYPE TY_PRICE + - RAISING {{P}}CX_PRINT_JOB_ERROR and {{P}}CX_PRINT_LIMIT +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. + +1. Check the input in this order. Raise the exception of the first rule that + fails. + 1. IV_PAGES is smaller than 1: raise {{P}}CX_PRINT_JOB_ERROR with the + reason PAGES. + 2. IV_COPIES is smaller than 1: raise {{P}}CX_PRINT_JOB_ERROR with the + reason COPIES. + 3. IV_PAPER is not 'A4', 'A3' or 'A5': raise {{P}}CX_PRINT_JOB_ERROR with + the reason PAPER. + 4. IV_COLOR is not 'C' or 'M': raise {{P}}CX_PRINT_JOB_ERROR with the + reason COLOR. + 5. IV_DEPT is not 'SALE', 'HR', 'IT' or 'PROD': raise + {{P}}CX_PRINT_JOB_ERROR with the reason DEPT. +2. The total number of pages is IV_PAGES multiplied by IV_COPIES. +3. If the total number of pages is greater than 5000, raise + {{P}}CX_PRINT_LIMIT with MV_PAGES equal to the total number of pages. +4. The price of one page is 0.03 for A5, 0.05 for A4 and 0.09 for A3. For the + color mode 'C', multiply the price of one page by 4. +5. The gross price is the total number of pages multiplied by the price of one + page. +6. Volume discount on the gross price: 10 per cent if the total number of + pages is 1000 or more, 5 per cent if the total number of pages is 500 or + more, otherwise no discount. +7. The net price is the gross price minus the discount. +8. If the net price is smaller than 1.00, the price is 1.00 (minimum charge). +9. Return the price. + +# 6. Constraints +- Release target: SAP_BASIS 8.16. +- 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 report, no function module, no CDS view. + +# 7. Acceptance +- The three classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}PRINT_JOB_PRICER. diff --git a/tasks_gen/eval/G0129/task.json b/tasks_gen/eval/G0129/task.json new file mode 100644 index 0000000..2a931af --- /dev/null +++ b/tasks_gen/eval/G0129/task.json @@ -0,0 +1,66 @@ +{ + "id": "G0129", + "category": "D", + "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}}CX_PRINT_JOB_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}CX_PRINT_LIMIT" + }, + { + "type": "CLAS", + "name": "{{P}}PRINT_JOB_PRICER" + } + ], + "out_of_scope": [ + "Database tables", + "Reports", + "Function modules", + "CDS views" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T_PRINT_HIDDEN", + "file": "hidden/t_print_hidden.clas.abap", + "description": "Hidden tests for the print job price" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_PRINT_JOB_ERROR", + "file": "reference/cx_print_job_error.clas.abap", + "description": "Invalid print job data" + }, + { + "type": "CLAS", + "name": "{{P}}CX_PRINT_LIMIT", + "file": "reference/cx_print_limit.clas.abap", + "description": "Print job above the page limit" + }, + { + "type": "CLAS", + "name": "{{P}}PRINT_JOB_PRICER", + "file": "reference/print_job_pricer.clas.abap", + "description": "Print job price", + "testclasses_file": "reference/print_job_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0130/generation.json b/tasks_gen/eval/G0130/generation.json new file mode 100644 index 0000000..1573d79 --- /dev/null +++ b/tasks_gen/eval/G0130/generation.json @@ -0,0 +1,33 @@ +{ + "id": "G0130", + "pool": "eval", + "object_type": "CLAS", + "category": "D", + "attempts": [ + { + "stage": "bundle", + "errors": [ + "seed/drug_master.clas.abap line 15: 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": 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/eval/G0130/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0130/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..8eb0d61 --- /dev/null +++ b/tasks_gen/eval/G0130/hidden/t02_hidden.clas.abap @@ -0,0 +1,156 @@ +CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC + FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}prescription_check. + + METHODS setup. + METHODS empty_prescription FOR TESTING. + METHODS expired_prescription FOR TESTING. + METHODS valid_on_last_day FOR TESTING. + METHODS unknown_drug FOR TESTING. + METHODS zero_quantity FOR TESTING. + METHODS zero_daily_dose FOR TESTING. + METHODS dose_above_max FOR TESTING. + METHODS duplicate_drug FOR TESTING. + METHODS expired_before_quantity FOR TESTING. + METHODS dose_before_duplicate FOR TESTING. + METHODS valid_prescription FOR TESTING. + METHODS message_texts FOR TESTING. + + METHODS valid_item + RETURNING VALUE(rt_item) TYPE {{p}}prescription_check=>tt_item. + METHODS expect_reason + IMPORTING iv_reason TYPE {{p}}cx_presc_error=>ty_reason + it_item TYPE {{p}}prescription_check=>tt_item + iv_as_of TYPE d DEFAULT '20240110'. + METHODS expect_no_error + IMPORTING it_item TYPE {{p}}prescription_check=>tt_item + iv_as_of TYPE d DEFAULT '20240110'. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}prescription_check( ). + ENDMETHOD. + + METHOD valid_item. + rt_item = VALUE #( ( drug_id = 'PARA500' quantity = 20 daily_dose = 1000 ) ). + ENDMETHOD. + + METHOD expect_reason. + DATA lv_raised TYPE abap_bool. + TRY. + mo_cut->validate( iv_issue_date = '20240101' + iv_valid_days = 30 + iv_as_of_date = iv_as_of + it_item = it_item ). + CATCH {{p}}cx_presc_error INTO DATA(lx_error). + lv_raised = abap_true. + cl_abap_unit_assert=>assert_equals( exp = iv_reason + act = lx_error->get_reason( ) ). + ENDTRY. + cl_abap_unit_assert=>assert_true( act = lv_raised + msg = |No exception with reason { iv_reason }| ). + ENDMETHOD. + + METHOD expect_no_error. + DATA lv_raised TYPE abap_bool. + TRY. + mo_cut->validate( iv_issue_date = '20240101' + iv_valid_days = 30 + iv_as_of_date = iv_as_of + it_item = it_item ). + CATCH {{p}}cx_presc_error. + lv_raised = abap_true. + ENDTRY. + cl_abap_unit_assert=>assert_false( act = lv_raised ). + ENDMETHOD. + + METHOD empty_prescription. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-empty + it_item = VALUE {{p}}prescription_check=>tt_item( ) ). + ENDMETHOD. + + METHOD expired_prescription. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-expired + it_item = valid_item( ) + iv_as_of = '20240201' ). + ENDMETHOD. + + METHOD valid_on_last_day. + expect_no_error( it_item = valid_item( ) + iv_as_of = '20240131' ). + ENDMETHOD. + + METHOD unknown_drug. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-unknown + it_item = VALUE #( ( drug_id = 'XXXXXX' quantity = 5 daily_dose = 10 ) ) ). + ENDMETHOD. + + METHOD zero_quantity. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-quantity + it_item = VALUE #( ( drug_id = 'PARA500' quantity = 0 daily_dose = 1000 ) ) ). + ENDMETHOD. + + METHOD zero_daily_dose. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-dose + it_item = VALUE #( ( drug_id = 'PARA500' quantity = 5 daily_dose = 0 ) ) ). + ENDMETHOD. + + METHOD dose_above_max. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-dose + it_item = VALUE #( ( drug_id = 'META50' quantity = 5 daily_dose = 250 ) ) ). + ENDMETHOD. + + METHOD duplicate_drug. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-duplicate + it_item = VALUE #( ( drug_id = 'PARA500' quantity = 5 daily_dose = 1000 ) + ( drug_id = 'PARA500' quantity = 5 daily_dose = 1000 ) ) ). + ENDMETHOD. + + METHOD expired_before_quantity. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-expired + it_item = VALUE #( ( drug_id = 'PARA500' quantity = 0 daily_dose = 1000 ) ) + iv_as_of = '20240201' ). + ENDMETHOD. + + METHOD dose_before_duplicate. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-dose + it_item = VALUE #( ( drug_id = 'META50' quantity = 5 daily_dose = 250 ) + ( drug_id = 'META50' quantity = 5 daily_dose = 100 ) ) ). + ENDMETHOD. + + METHOD valid_prescription. + expect_no_error( it_item = VALUE #( ( drug_id = 'PARA500' quantity = 20 daily_dose = 4000 ) + ( drug_id = 'IBU400' quantity = 10 daily_dose = 1200 ) ) ). + ENDMETHOD. + + METHOD message_texts. + DATA lo_error TYPE REF TO {{p}}cx_presc_error. + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-empty ). + cl_abap_unit_assert=>assert_equals( exp = 'The prescription has no items' + act = lo_error->get_message_text( ) ). + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-expired ). + cl_abap_unit_assert=>assert_equals( exp = 'The prescription is expired' + act = lo_error->get_message_text( ) ). + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-quantity ). + cl_abap_unit_assert=>assert_equals( exp = 'The quantity is not positive' + act = lo_error->get_message_text( ) ). + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-unknown ). + cl_abap_unit_assert=>assert_equals( exp = 'The drug is not in the drug master' + act = lo_error->get_message_text( ) ). + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-dose ). + cl_abap_unit_assert=>assert_equals( exp = 'The daily dose is not allowed' + act = lo_error->get_message_text( ) ). + + lo_error = NEW #( iv_reason = {{p}}cx_presc_error=>c_reason-duplicate ). + cl_abap_unit_assert=>assert_equals( exp = 'The drug appears more than once' + act = lo_error->get_message_text( ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0130/reference/cx_presc_error.clas.abap b/tasks_gen/eval/G0130/reference/cx_presc_error.clas.abap new file mode 100644 index 0000000..cd14ebe --- /dev/null +++ b/tasks_gen/eval/G0130/reference/cx_presc_error.clas.abap @@ -0,0 +1,60 @@ +CLASS {{p}}cx_presc_error DEFINITION + PUBLIC + INHERITING FROM cx_root + FINAL + CREATE PUBLIC. + + PUBLIC SECTION. + TYPES ty_reason TYPE c LENGTH 12. + + CONSTANTS: + BEGIN OF c_reason, + empty TYPE ty_reason VALUE 'EMPTY', + expired TYPE ty_reason VALUE 'EXPIRED', + quantity TYPE ty_reason VALUE 'QUANTITY', + unknown TYPE ty_reason VALUE 'UNKNOWN', + dose TYPE ty_reason VALUE 'DOSE', + duplicate TYPE ty_reason VALUE 'DUPLICATE', + END OF c_reason. + + METHODS constructor + IMPORTING iv_reason TYPE ty_reason. + + METHODS get_reason + RETURNING VALUE(rv_reason) TYPE ty_reason. + + METHODS get_message_text + RETURNING VALUE(rv_text) TYPE string. + + PRIVATE SECTION. + DATA mv_reason TYPE ty_reason. +ENDCLASS. + + +CLASS {{p}}cx_presc_error IMPLEMENTATION. + METHOD constructor. + super->constructor( ). + mv_reason = iv_reason. + ENDMETHOD. + + METHOD get_reason. + rv_reason = mv_reason. + ENDMETHOD. + + METHOD get_message_text. + CASE mv_reason. + WHEN c_reason-empty. + rv_text = 'The prescription has no items'. + WHEN c_reason-expired. + rv_text = 'The prescription is expired'. + WHEN c_reason-quantity. + rv_text = 'The quantity is not positive'. + WHEN c_reason-unknown. + rv_text = 'The drug is not in the drug master'. + WHEN c_reason-dose. + rv_text = 'The daily dose is not allowed'. + WHEN c_reason-duplicate. + rv_text = 'The drug appears more than once'. + ENDCASE. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0130/reference/prescription_check.clas.abap b/tasks_gen/eval/G0130/reference/prescription_check.clas.abap new file mode 100644 index 0000000..1322935 --- /dev/null +++ b/tasks_gen/eval/G0130/reference/prescription_check.clas.abap @@ -0,0 +1,103 @@ +CLASS {{p}}prescription_check DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_drug_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_item, + drug_id TYPE ty_drug_id, + quantity TYPE i, + daily_dose TYPE i, + END OF ty_item, + tt_item TYPE STANDARD TABLE OF ty_item WITH EMPTY KEY. + + METHODS constructor. + + METHODS validate + IMPORTING iv_issue_date TYPE d + iv_valid_days TYPE i + iv_as_of_date TYPE d + it_item TYPE tt_item + RAISING {{p}}cx_presc_error. + + PRIVATE SECTION. + DATA mo_drug_master TYPE REF TO {{p}}drug_master. + + METHODS expiry_date + IMPORTING iv_issue_date TYPE d + iv_valid_days TYPE i + RETURNING VALUE(rv_expiry) TYPE d. + + METHODS check_items + IMPORTING it_item TYPE tt_item + RAISING {{p}}cx_presc_error. + + METHODS check_item + IMPORTING is_item TYPE ty_item + RAISING {{p}}cx_presc_error. + + METHODS check_duplicates + IMPORTING it_item TYPE tt_item + RAISING {{p}}cx_presc_error. +ENDCLASS. + + +CLASS {{p}}prescription_check IMPLEMENTATION. + METHOD constructor. + mo_drug_master = NEW {{p}}drug_master( ). + ENDMETHOD. + + METHOD validate. + IF it_item IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-empty. + ENDIF. + + IF iv_as_of_date > expiry_date( iv_issue_date = iv_issue_date + iv_valid_days = iv_valid_days ). + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-expired. + ENDIF. + + check_items( it_item ). + check_duplicates( it_item ). + ENDMETHOD. + + METHOD expiry_date. + rv_expiry = iv_issue_date + iv_valid_days. + ENDMETHOD. + + METHOD check_items. + LOOP AT it_item INTO DATA(ls_item). + check_item( ls_item ). + ENDLOOP. + ENDMETHOD. + + METHOD check_item. + IF is_item-quantity < 1. + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-quantity. + ENDIF. + + DATA(ls_drug) = mo_drug_master->get_drug( is_item-drug_id ). + IF ls_drug IS INITIAL. + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-unknown. + ENDIF. + + IF is_item-daily_dose < 1 OR is_item-daily_dose > ls_drug-max_daily_dose. + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-dose. + ENDIF. + ENDMETHOD. + + METHOD check_duplicates. + DATA lt_seen TYPE HASHED TABLE OF ty_item WITH UNIQUE KEY drug_id. + LOOP AT it_item INTO DATA(ls_item). + INSERT ls_item INTO TABLE lt_seen. + IF sy-subrc <> 0. + RAISE EXCEPTION TYPE {{p}}cx_presc_error + EXPORTING iv_reason = {{p}}cx_presc_error=>c_reason-duplicate. + ENDIF. + ENDLOOP. + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0130/reference/prescription_check.testclasses.abap b/tasks_gen/eval/G0130/reference/prescription_check.testclasses.abap new file mode 100644 index 0000000..eefddd7 --- /dev/null +++ b/tasks_gen/eval/G0130/reference/prescription_check.testclasses.abap @@ -0,0 +1,104 @@ +CLASS ltc_presc_check DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}prescription_check. + + METHODS setup. + METHODS valid_prescription FOR TESTING. + METHODS empty_prescription FOR TESTING. + METHODS expired_prescription FOR TESTING. + METHODS unknown_drug FOR TESTING. + METHODS bad_quantity FOR TESTING. + METHODS bad_dose FOR TESTING. + METHODS duplicate_drug FOR TESTING. + METHODS message_of_empty FOR TESTING. + + METHODS valid_item + RETURNING VALUE(rt_item) TYPE {{p}}prescription_check=>tt_item. + METHODS expect_reason + IMPORTING iv_reason TYPE {{p}}cx_presc_error=>ty_reason + it_item TYPE {{p}}prescription_check=>tt_item. +ENDCLASS. + + +CLASS ltc_presc_check IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}prescription_check( ). + ENDMETHOD. + + METHOD valid_item. + rt_item = VALUE #( ( drug_id = 'IBU400' quantity = 10 daily_dose = 1200 ) ). + ENDMETHOD. + + METHOD expect_reason. + DATA lv_raised TYPE abap_bool. + TRY. + mo_cut->validate( iv_issue_date = '20240101' + iv_valid_days = 30 + iv_as_of_date = '20240110' + it_item = it_item ). + CATCH {{p}}cx_presc_error INTO DATA(lx_error). + lv_raised = abap_true. + cl_abap_unit_assert=>assert_equals( exp = iv_reason + act = lx_error->get_reason( ) ). + ENDTRY. + cl_abap_unit_assert=>assert_true( act = lv_raised ). + ENDMETHOD. + + METHOD valid_prescription. + TRY. + mo_cut->validate( iv_issue_date = '20240101' + iv_valid_days = 30 + iv_as_of_date = '20240110' + it_item = valid_item( ) ). + CATCH {{p}}cx_presc_error. + cl_abap_unit_assert=>fail( msg = 'Unexpected exception' ). + ENDTRY. + ENDMETHOD. + + METHOD empty_prescription. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-empty + it_item = VALUE {{p}}prescription_check=>tt_item( ) ). + ENDMETHOD. + + METHOD expired_prescription. + DATA lv_raised TYPE abap_bool. + TRY. + mo_cut->validate( iv_issue_date = '20240101' + iv_valid_days = 30 + iv_as_of_date = '20240201' + it_item = valid_item( ) ). + CATCH {{p}}cx_presc_error INTO DATA(lx_error). + lv_raised = abap_true. + cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_presc_error=>c_reason-expired + act = lx_error->get_reason( ) ). + ENDTRY. + cl_abap_unit_assert=>assert_true( act = lv_raised ). + ENDMETHOD. + + METHOD unknown_drug. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-unknown + it_item = VALUE #( ( drug_id = 'NODRUG' quantity = 5 daily_dose = 10 ) ) ). + ENDMETHOD. + + METHOD bad_quantity. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-quantity + it_item = VALUE #( ( drug_id = 'IBU400' quantity = 0 daily_dose = 1200 ) ) ). + ENDMETHOD. + + METHOD bad_dose. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-dose + it_item = VALUE #( ( drug_id = 'META50' quantity = 5 daily_dose = 500 ) ) ). + ENDMETHOD. + + METHOD duplicate_drug. + expect_reason( iv_reason = {{p}}cx_presc_error=>c_reason-duplicate + it_item = VALUE #( ( drug_id = 'IBU400' quantity = 10 daily_dose = 1200 ) + ( drug_id = 'IBU400' quantity = 10 daily_dose = 1200 ) ) ). + ENDMETHOD. + + METHOD message_of_empty. + DATA(lo_error) = NEW {{p}}cx_presc_error( iv_reason = {{p}}cx_presc_error=>c_reason-empty ). + cl_abap_unit_assert=>assert_equals( exp = 'The prescription has no items' + act = lo_error->get_message_text( ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0130/seed/drug_master.clas.abap b/tasks_gen/eval/G0130/seed/drug_master.clas.abap new file mode 100644 index 0000000..ee82309 --- /dev/null +++ b/tasks_gen/eval/G0130/seed/drug_master.clas.abap @@ -0,0 +1,38 @@ +CLASS {{p}}drug_master DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES ty_drug_id TYPE c LENGTH 10. + + TYPES: + BEGIN OF ty_drug, + drug_id TYPE ty_drug_id, + max_daily_dose TYPE i, + END OF ty_drug, + tt_drug TYPE STANDARD TABLE OF ty_drug WITH EMPTY KEY. + + METHODS get_drug + IMPORTING iv_drug_id TYPE ty_drug_id + RETURNING VALUE(rs_drug) TYPE ty_drug. + + PRIVATE SECTION. + METHODS get_all_drugs + RETURNING VALUE(rt_drug) TYPE tt_drug. +ENDCLASS. + + +CLASS {{p}}drug_master IMPLEMENTATION. + METHOD get_drug. + DATA(lt_drug) = get_all_drugs( ). + READ TABLE lt_drug INTO rs_drug WITH KEY drug_id = iv_drug_id. + IF sy-subrc <> 0. + CLEAR rs_drug. + ENDIF. + ENDMETHOD. + + METHOD get_all_drugs. + rt_drug = VALUE #( ( drug_id = 'PARA500' max_daily_dose = 4000 ) + ( drug_id = 'IBU400' max_daily_dose = 1200 ) + ( drug_id = 'AMOX500' max_daily_dose = 1500 ) + ( drug_id = 'META50' max_daily_dose = 200 ) + ( drug_id = 'SALB100' max_daily_dose = 800 ) ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0130/spec.md b/tasks_gen/eval/G0130/spec.md new file mode 100644 index 0000000..eb4cad6 --- /dev/null +++ b/tasks_gen/eval/G0130/spec.md @@ -0,0 +1,84 @@ +# 1. Goal +A pharmacy dispenses prescription items. Before the pharmacy dispenses a +prescription, it validates the prescription. The validation raises a class-based +exception with one reason and one message for each problem. The pharmacy program +needs the validation class and the exception class. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}DRUG_MASTER is in package $TMP. It is active. It is the drug + master data of the pharmacy. Do not change it. + - Public type TY_DRUG_ID: character, length 10. + - Public structure TY_DRUG with the components DRUG_ID (TY_DRUG_ID) and + MAX_DAILY_DOSE (integer, milligram). + - Public method GET_DRUG with the importing parameter IV_DRUG_ID (TY_DRUG_ID) + and the returning parameter RS_DRUG (TY_DRUG). If the drug is not in the + master data, RS_DRUG is initial. +- The drug master data contains these drugs and maximum daily doses: + PARA500 4000, IBU400 1200, AMOX500 1500, META50 200, SALB100 800. +- A prescription has an issue date, a number of valid days and items. Each item + has a drug, a quantity and a daily dose. + +# 4. Contract +## 4.1 Exception class {{P}}CX_PRESC_ERROR +- Create the class in package $TMP. +- The class inherits from CX_ROOT. +- Public type TY_REASON: character, length 12. +- Public constant structure C_REASON with the components EMPTY, EXPIRED, + QUANTITY, UNKNOWN, DOSE and DUPLICATE. Each component has the type TY_REASON + and the value of its own name. +- Public constructor with the importing parameter IV_REASON (TY_REASON). +- Public method GET_REASON with the returning parameter RV_REASON (TY_REASON). +- Public method GET_MESSAGE_TEXT with the returning parameter RV_TEXT (string). + GET_MESSAGE_TEXT returns the message of the reason: + - EMPTY: The prescription has no items + - EXPIRED: The prescription is expired + - QUANTITY: The quantity is not positive + - UNKNOWN: The drug is not in the drug master + - DOSE: The daily dose is not allowed + - DUPLICATE: The drug appears more than once + +## 4.2 Check class {{P}}PRESCRIPTION_CHECK +- Create the class in package $TMP. +- The class is public and final. It has a public constructor without parameters. +- Public type TY_DRUG_ID: character, length 10. +- Public structure TY_ITEM with the components DRUG_ID (TY_DRUG_ID), QUANTITY + (integer) and DAILY_DOSE (integer). +- Public table type TT_ITEM: standard table of TY_ITEM with empty key. +- Public method VALIDATE with these importing parameters: + - IV_ISSUE_DATE (type d) + - IV_VALID_DAYS (integer) + - IV_AS_OF_DATE (type d) + - IT_ITEM (TT_ITEM) + and the addition RAISING {{P}}CX_PRESC_ERROR. +- Do not add other public methods. + +# 5. Business rules +VALIDATE applies the rules in this sequence. It raises {{P}}CX_PRESC_ERROR with +the reason of the first rule that fails. +1. If IT_ITEM is empty, the reason is EMPTY. +2. If IV_AS_OF_DATE is later than IV_ISSUE_DATE plus IV_VALID_DAYS days, the + reason is EXPIRED. +3. Process the items in the sequence of the table. For each item: + 3.1 If QUANTITY is less than 1, the reason is QUANTITY. + 3.2 If DRUG_ID is not in the drug master data, the reason is UNKNOWN. + 3.3 If DAILY_DOSE is less than 1, or if DAILY_DOSE is greater than the + maximum daily dose of the drug, the reason is DOSE. +4. If one DRUG_ID occurs in more than one item, the reason is DUPLICATE. +5. Else VALIDATE returns without an exception. +The dates are always valid dates. + +# 6. Constraints +- Release target: SAP_BASIS 816 (ABAP Platform 2025). +- Package: $TMP. Do not use a transport request. +- Coding standards: Clean ABAP. Methods below 40 statements. No global + variables. Pass large parameters by reference. No comment that restates the + code. +- Out of scope: do not change {{P}}DRUG_MASTER. + +# 7. Acceptance +- Both classes are active and have no syntax error. +- The hidden tests pass. +- Write your own ABAP Unit tests for {{P}}PRESCRIPTION_CHECK. diff --git a/tasks_gen/eval/G0130/task.json b/tasks_gen/eval/G0130/task.json new file mode 100644 index 0000000..39d1967 --- /dev/null +++ b/tasks_gen/eval/G0130/task.json @@ -0,0 +1,60 @@ +{ + "id": "G0130", + "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}}DRUG_MASTER", + "file": "seed/drug_master.clas.abap", + "description": "Drug master data of the pharmacy" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}CX_PRESC_ERROR" + }, + { + "type": "CLAS", + "name": "{{P}}PRESCRIPTION_CHECK" + } + ], + "out_of_scope": [ + "{{P}}DRUG_MASTER" + ], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "T02 hidden tests" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}CX_PRESC_ERROR", + "file": "reference/cx_presc_error.clas.abap", + "description": "Prescription exception with reason and message" + }, + { + "type": "CLAS", + "name": "{{P}}PRESCRIPTION_CHECK", + "file": "reference/prescription_check.clas.abap", + "description": "Prescription check", + "testclasses_file": "reference/prescription_check.testclasses.abap" + } + ], + "craft_checks": [ + "method_length", + "db_operation_in_loop" + ] +} \ No newline at end of file diff --git a/tasks_gen/eval/G0188/faulty/m0_laundry_pricer.clas.abap b/tasks_gen/eval/G0188/faulty/m0_laundry_pricer.clas.abap new file mode 100644 index 0000000..9317e25 --- /dev/null +++ b/tasks_gen/eval/G0188/faulty/m0_laundry_pricer.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS: + c_rate_wash TYPE ty_amount VALUE '2.50', + c_rate_dry TYPE ty_amount VALUE '4.00', + c_rate_iron TYPE ty_amount VALUE '1.50', + c_express_fee TYPE ty_amount VALUE '5.00', + c_min_price TYPE ty_amount VALUE '10.00', + c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF iv_member <> abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + IF iv_express = abap_true. + lv_price = lv_price + c_express_fee. + ENDIF. + + IF lv_price < c_min_price. + lv_price = c_min_price. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_wash. + rv_rate = c_rate_wash. + WHEN c_service_dry. + rv_rate = c_rate_dry. + WHEN c_service_iron. + rv_rate = c_rate_iron. + WHEN OTHERS. + rv_rate = c_rate_wash. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/faulty/m1_laundry_pricer.clas.abap b/tasks_gen/eval/G0188/faulty/m1_laundry_pricer.clas.abap new file mode 100644 index 0000000..a01d8ee --- /dev/null +++ b/tasks_gen/eval/G0188/faulty/m1_laundry_pricer.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS: + c_rate_wash TYPE ty_amount VALUE '2.50', + c_rate_dry TYPE ty_amount VALUE '4.00', + c_rate_iron TYPE ty_amount VALUE '1.50', + c_express_fee TYPE ty_amount VALUE '5.00', + c_min_price TYPE ty_amount VALUE '10.00', + c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF iv_member = abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + IF iv_express = abap_false. + lv_price = lv_price + c_express_fee. + ENDIF. + + IF lv_price < c_min_price. + lv_price = c_min_price. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_wash. + rv_rate = c_rate_wash. + WHEN c_service_dry. + rv_rate = c_rate_dry. + WHEN c_service_iron. + rv_rate = c_rate_iron. + WHEN OTHERS. + rv_rate = c_rate_wash. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/faulty/m2_laundry_pricer.clas.abap b/tasks_gen/eval/G0188/faulty/m2_laundry_pricer.clas.abap new file mode 100644 index 0000000..65682ce --- /dev/null +++ b/tasks_gen/eval/G0188/faulty/m2_laundry_pricer.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS: + c_rate_wash TYPE ty_amount VALUE '2.50', + c_rate_dry TYPE ty_amount VALUE '4.00', + c_rate_iron TYPE ty_amount VALUE '1.50', + c_express_fee TYPE ty_amount VALUE '5.00', + c_min_price TYPE ty_amount VALUE '10.00', + c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF iv_member = abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + IF iv_express = abap_true. + lv_price = lv_price - c_express_fee. + ENDIF. + + IF lv_price < c_min_price. + lv_price = c_min_price. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_wash. + rv_rate = c_rate_wash. + WHEN c_service_dry. + rv_rate = c_rate_dry. + WHEN c_service_iron. + rv_rate = c_rate_iron. + WHEN OTHERS. + rv_rate = c_rate_wash. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/faulty/m3_laundry_pricer.clas.abap b/tasks_gen/eval/G0188/faulty/m3_laundry_pricer.clas.abap new file mode 100644 index 0000000..9047115 --- /dev/null +++ b/tasks_gen/eval/G0188/faulty/m3_laundry_pricer.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS: + c_rate_wash TYPE ty_amount VALUE '2.50', + c_rate_dry TYPE ty_amount VALUE '4.00', + c_rate_iron TYPE ty_amount VALUE '1.50', + c_express_fee TYPE ty_amount VALUE '5.00', + c_min_price TYPE ty_amount VALUE '10.00', + c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF iv_member = abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + IF iv_express = abap_true. + lv_price = lv_price + c_express_fee. + ENDIF. + + IF lv_price >= c_min_price. + lv_price = c_min_price. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_wash. + rv_rate = c_rate_wash. + WHEN c_service_dry. + rv_rate = c_rate_dry. + WHEN c_service_iron. + rv_rate = c_rate_iron. + WHEN OTHERS. + rv_rate = c_rate_wash. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/generation.json b/tasks_gen/eval/G0188/generation.json new file mode 100644 index 0000000..43cf087 --- /dev/null +++ b/tasks_gen/eval/G0188/generation.json @@ -0,0 +1,24 @@ +{ + "id": "G0188", + "pool": "eval", + "object_type": "CLAS", + "category": "I", + "attempts": [ + { + "stage": "validate", + "oracle": null, + "null": null + }, + { + "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/eval/G0188/hidden/t02_hidden.clas.abap b/tasks_gen/eval/G0188/hidden/t02_hidden.clas.abap new file mode 100644 index 0000000..0522242 --- /dev/null +++ b/tasks_gen/eval/G0188/hidden/t02_hidden.clas.abap @@ -0,0 +1,113 @@ +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}}laundry_pricer. + METHODS setup. + METHODS wash_basic FOR TESTING. + METHODS dry_basic FOR TESTING. + METHODS iron_basic FOR TESTING. + METHODS iron_above_min FOR TESTING. + METHODS express_no_member FOR TESTING. + METHODS member_no_express FOR TESTING. + METHODS member_then_express FOR TESTING. + METHODS min_after_discount FOR TESTING. + METHODS unknown_service FOR TESTING. + METHODS rounding FOR TESTING. +ENDCLASS. + + +CLASS {{p}}t02_hidden IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}laundry_pricer( ). + ENDMETHOD. + + METHOD wash_basic. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD dry_basic. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '3.00' + iv_service = {{p}}laundry_pricer=>c_service_dry + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '12.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD iron_basic. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '5.00' + iv_service = {{p}}laundry_pricer=>c_service_iron + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD iron_above_min. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '8.00' + iv_service = {{p}}laundry_pricer=>c_service_iron + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '12.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD express_no_member. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '4.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_true + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '15.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD member_no_express. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '5.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_false + iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '11.25' ) + act = lv_price ). + ENDMETHOD. + + METHOD member_then_express. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '4.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_true + iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '14.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD min_after_discount. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_false + iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD unknown_service. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = 'XXXX' + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD rounding. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.23' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_true + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.58' ) + act = lv_price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/mutation.json b/tasks_gen/eval/G0188/mutation.json new file mode 100644 index 0000000..0cecf35 --- /dev/null +++ b/tasks_gen/eval/G0188/mutation.json @@ -0,0 +1,74 @@ +{ + "task": "G0188", + "mutants": [ + { + "object": "{{P}}LAUNDRY_PRICER", + "mutant": "line 48: = -> <> (eq)", + "status": "killed", + "hidden": "4/10", + "failed_tests": [ + "DRY_BASIC", + "EXPRESS_NO_MEMBER", + "IRON_ABOVE_MIN", + "MEMBER_NO_EXPRESS", + "MEMBER_THEN_EXPRESS", + "ROUNDING" + ] + }, + { + "object": "{{P}}LAUNDRY_PRICER", + "mutant": "line 52: abap_true -> abap_false (bool)", + "status": "killed", + "hidden": "3/10", + "failed_tests": [ + "DRY_BASIC", + "EXPRESS_NO_MEMBER", + "IRON_ABOVE_MIN", + "IRON_BASIC", + "MEMBER_NO_EXPRESS", + "MEMBER_THEN_EXPRESS", + "ROUNDING" + ] + }, + { + "object": "{{P}}LAUNDRY_PRICER", + "mutant": "line 53: + -> - (arith)", + "status": "killed", + "hidden": "7/10", + "failed_tests": [ + "EXPRESS_NO_MEMBER", + "MEMBER_THEN_EXPRESS", + "ROUNDING" + ] + }, + { + "object": "{{P}}LAUNDRY_PRICER", + "mutant": "line 56: < -> >= (rel)", + "status": "killed", + "hidden": "0/10", + "failed_tests": [ + "DRY_BASIC", + "EXPRESS_NO_MEMBER", + "IRON_ABOVE_MIN", + "IRON_BASIC", + "MEMBER_NO_EXPRESS", + "MEMBER_THEN_EXPRESS", + "MIN_AFTER_DISCOUNT", + "ROUNDING", + "UNKNOWN_SERVICE", + "WASH_BASIC" + ] + }, + { + "object": "{{P}}LAUNDRY_PRICER", + "mutant": "line 77: 2 -> 3 (const)", + "status": "survived", + "hidden": "10/10", + "failed_tests": [] + } + ], + "valid": 5, + "killed": 4, + "kill_rate": 0.8, + "ok": true +} \ No newline at end of file diff --git a/tasks_gen/eval/G0188/reference/laundry_pricer.clas.abap b/tasks_gen/eval/G0188/reference/laundry_pricer.clas.abap new file mode 100644 index 0000000..d2a3d16 --- /dev/null +++ b/tasks_gen/eval/G0188/reference/laundry_pricer.clas.abap @@ -0,0 +1,79 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS: + c_rate_wash TYPE ty_amount VALUE '2.50', + c_rate_dry TYPE ty_amount VALUE '4.00', + c_rate_iron TYPE ty_amount VALUE '1.50', + c_express_fee TYPE ty_amount VALUE '5.00', + c_min_price TYPE ty_amount VALUE '10.00', + c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF iv_member = abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + IF iv_express = abap_true. + lv_price = lv_price + c_express_fee. + ENDIF. + + IF lv_price < c_min_price. + lv_price = c_min_price. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_wash. + rv_rate = c_rate_wash. + WHEN c_service_dry. + rv_rate = c_rate_dry. + WHEN c_service_iron. + rv_rate = c_rate_iron. + WHEN OTHERS. + rv_rate = c_rate_wash. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/reference/laundry_pricer.testclasses.abap b/tasks_gen/eval/G0188/reference/laundry_pricer.testclasses.abap new file mode 100644 index 0000000..6f985ae --- /dev/null +++ b/tasks_gen/eval/G0188/reference/laundry_pricer.testclasses.abap @@ -0,0 +1,62 @@ +CLASS ltc_laundry_pricer DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. + PRIVATE SECTION. + DATA mo_cut TYPE REF TO {{p}}laundry_pricer. + METHODS setup. + METHODS wash_basic FOR TESTING. + METHODS iron_rate FOR TESTING. + METHODS member_then_express FOR TESTING. + METHODS min_after_discount FOR TESTING. + METHODS unknown_service FOR TESTING. +ENDCLASS. + + +CLASS ltc_laundry_pricer IMPLEMENTATION. + METHOD setup. + mo_cut = NEW {{p}}laundry_pricer( ). + ENDMETHOD. + + METHOD wash_basic. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD iron_rate. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '8.00' + iv_service = {{p}}laundry_pricer=>c_service_iron + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '12.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD member_then_express. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '4.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_true + iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '14.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD min_after_discount. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = {{p}}laundry_pricer=>c_service_wash + iv_express = abap_false + iv_member = abap_true ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. + + METHOD unknown_service. + DATA(lv_price) = mo_cut->calculate( iv_weight_kg = '2.00' + iv_service = 'XXXX' + iv_express = abap_false + iv_member = abap_false ). + cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}laundry_pricer=>ty_amount( '10.00' ) + act = lv_price ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/review.json b/tasks_gen/eval/G0188/review.json new file mode 100644 index 0000000..546ab66 --- /dev/null +++ b/tasks_gen/eval/G0188/review.json @@ -0,0 +1,7 @@ +{ + "decision": "accept", + "note": "Real in-place I task: wash/dry rates, unknown service and rounding come only from the legacy code; tests check them. Survived mutant: round dec const.", + "by": "Claude", + "date": "2026-10-03", + "failed_checks": {} +} \ No newline at end of file diff --git a/tasks_gen/eval/G0188/seed/laundry_pricer.clas.abap b/tasks_gen/eval/G0188/seed/laundry_pricer.clas.abap new file mode 100644 index 0000000..8290ae1 --- /dev/null +++ b/tasks_gen/eval/G0188/seed/laundry_pricer.clas.abap @@ -0,0 +1,71 @@ +CLASS {{p}}laundry_pricer DEFINITION PUBLIC FINAL CREATE PUBLIC. + PUBLIC SECTION. + TYPES: + ty_weight_kg TYPE p LENGTH 5 DECIMALS 2, + ty_amount TYPE p LENGTH 8 DECIMALS 2, + ty_service TYPE c LENGTH 4. + CONSTANTS: + c_service_wash TYPE ty_service VALUE 'WASH', + c_service_dry TYPE ty_service VALUE 'DRY', + c_service_iron TYPE ty_service VALUE 'IRON'. + METHODS calculate + IMPORTING + iv_weight_kg TYPE ty_weight_kg + iv_service TYPE ty_service + iv_express TYPE abap_bool + iv_member TYPE abap_bool + RETURNING + VALUE(rv_price) TYPE ty_amount. + PRIVATE SECTION. + CONSTANTS c_member_discount TYPE ty_amount VALUE '0.90'. + METHODS rate_per_kg + IMPORTING iv_service TYPE ty_service + RETURNING VALUE(rv_rate) TYPE ty_amount. + METHODS round_currency + IMPORTING iv_amount TYPE ty_amount + RETURNING VALUE(rv_amount) TYPE ty_amount. +ENDCLASS. + + +CLASS {{p}}laundry_pricer IMPLEMENTATION. + METHOD calculate. + IF iv_weight_kg <= 0. + rv_price = 0. + RETURN. + ENDIF. + + DATA lv_rate TYPE ty_amount. + DATA lv_price TYPE ty_amount. + lv_rate = rate_per_kg( iv_service ). + lv_price = iv_weight_kg * lv_rate. + + IF lv_price < 10. + lv_price = 10. + ENDIF. + + IF iv_express = abap_true. + lv_price = lv_price + 5. + ENDIF. + + IF iv_member = abap_true. + lv_price = lv_price * c_member_discount. + ENDIF. + + rv_price = round_currency( lv_price ). + ENDMETHOD. + + METHOD rate_per_kg. + CASE iv_service. + WHEN c_service_dry. + rv_rate = '4.00'. + WHEN c_service_iron. + rv_rate = '2.50'. + WHEN OTHERS. + rv_rate = '2.50'. + ENDCASE. + ENDMETHOD. + + METHOD round_currency. + rv_amount = round( val = iv_amount dec = 2 ). + ENDMETHOD. +ENDCLASS. diff --git a/tasks_gen/eval/G0188/spec.md b/tasks_gen/eval/G0188/spec.md new file mode 100644 index 0000000..eacb5b0 --- /dev/null +++ b/tasks_gen/eval/G0188/spec.md @@ -0,0 +1,49 @@ +# 1. Goal +Fix the class {{P}}LAUNDRY_PRICER. A laundry service uses the class to calculate the price of a laundry order. Users report three defects: +- A member pays the wrong price for a small order. +- A member gets a discount on the express surcharge. +- Ironing is much too expensive. +The class must calculate the correct price for every order. + +# 2. Open questions +None. + +# 3. Context +- The class {{P}}LAUNDRY_PRICER exists in package $TMP. It is active. +- The class is public and final. It has a public method CALCULATE with the + importing parameters iv_weight_kg, iv_service, iv_express, iv_member and the + returning parameter rv_price. +- The class has the public types ty_weight_kg, ty_amount and ty_service and the + public constants c_service_wash, c_service_dry and c_service_iron. +- The class has the private methods rate_per_kg and round_currency. +- A laundry order has a weight in kg, a service, an express flag and a member + flag. The services are WASH, DRY and IRON. + +# 4. Contract +- Fix the class {{P}}LAUNDRY_PRICER in place. +- Do not change the public method signature, the public types or the public + constants. +- Do not add other public methods. + +# 5. Business rules +Apply the rules in this sequence. All other behavior stays the same. +1. The base price is the weight multiplied by the rate per kg. A member gets a + discount of 10 percent on the base price. An express order costs a fixed + surcharge of 5.00. The member discount does not apply to the express + surcharge. Add the express surcharge after the member discount. +2. The minimum order value is 10.00. Apply the minimum after all discounts and + surcharges. If the price after all discounts and surcharges is below 10.00, + the price is 10.00. +3. The ironing service uses its own rate. The rate for ironing is 1.50 per kg. + +# 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 the public method signature, the public types or + the public constants. + +# 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/eval/G0188/task.json b/tasks_gen/eval/G0188/task.json new file mode 100644 index 0000000..ba5402f --- /dev/null +++ b/tasks_gen/eval/G0188/task.json @@ -0,0 +1,47 @@ +{ + "id": "G0188", + "category": "I", + "object_type": "CLAS", + "difficulty": 2, + "release_target": "v816", + "expected_outcome": "implement", + "budget": { + "max_tool_calls": 40, + "max_activations": 10 + }, + "seed": [ + { + "type": "CLAS", + "name": "{{P}}LAUNDRY_PRICER", + "file": "seed/laundry_pricer.clas.abap", + "description": "Faulty laundry price calculator" + } + ], + "contract": [ + { + "type": "CLAS", + "name": "{{P}}LAUNDRY_PRICER" + } + ], + "out_of_scope": [], + "hidden_tests": [ + { + "type": "CLAS", + "name": "{{P}}T02_HIDDEN", + "file": "hidden/t02_hidden.clas.abap", + "description": "Hidden tests for the laundry price calculator" + } + ], + "reference": [ + { + "type": "CLAS", + "name": "{{P}}LAUNDRY_PRICER", + "file": "reference/laundry_pricer.clas.abap", + "description": "Fixed laundry price calculator", + "testclasses_file": "reference/laundry_pricer.testclasses.abap" + } + ], + "craft_checks": [ + "method_length" + ] +} \ No newline at end of file