Restore faz1/yol-haritasi docs (overwritten by copy); stage1 docs; G0174 new gap; stage1 25-task subset

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-03 17:33:55 +02:00
parent 01e99158a9
commit 65322f27ff
44 changed files with 2137 additions and 855 deletions

View File

@@ -0,0 +1,83 @@
# Stage 1: step prompts
Preparation (one time): copy `stage1-training-task.md` to
`~/projects/abap-llm/harness/docs/stage1-training-task.md`.
Use Sonnet. Before each prompt: `/clear`. Each prompt is one session.
---
## A — Setup and data (A4H: no change needed)
```
Read CLAUDE.md and docs/stage1-training-task.md. Do only Step 0 and Step 1.
Create train/STATE.md: write what you did, the model id, the paths, the
report of Step 1, and the next step. Commit. Then stop.
```
## B1 — Baseline, start (A4H must run)
```
Read CLAUDE.md, docs/stage1-training-task.md and train/STATE.md. Do Step 2.
Start the eval runs in the background with a log file and a macOS
notification at the end. Measure the valid loss of the base model in the
same background job, after the eval runs. Update train/STATE.md with the
log path and the job command. Then stop. Do not poll.
```
## B2 — Baseline, results (after the notification)
```
Read CLAUDE.md and train/STATE.md. Read only the last 50 lines of the log.
Write runs/stage1/baseline.json. Update train/STATE.md with the results.
Commit. Show me a short summary. Then stop.
```
## C — Training test (stop A4H first)
```
Read CLAUDE.md, docs/stage1-training-task.md and train/STATE.md. A4H is
stopped. Check that no other model is loaded. Do only the short test of
Step 3 (20 iterations). Report peak memory, time per iteration, and the
time estimate for the full run. If memory is not sufficient, give me
options and do not change the settings. Update train/STATE.md. Then stop.
```
## D1 — Training, start (after my approval)
```
Read CLAUDE.md, docs/stage1-training-task.md and train/STATE.md. Start the
full training run of Step 3 in the background with a log file and a macOS
notification at the end. Use the settings from train/config.yaml. Update
train/STATE.md with the log path and the adapter path. Then stop. Do not
poll.
```
## D2 — Training, results (after the notification)
```
Read CLAUDE.md and train/STATE.md. Read only the valid loss lines and the
last 30 lines of the log. Select the adapter with the lowest valid loss.
Update train/STATE.md. Commit. Show me the valid loss curve as a short
table. Then stop.
```
## E1 — Measure again, start (start A4H first)
```
Read CLAUDE.md, docs/stage1-training-task.md and train/STATE.md. A4H runs.
Do Step 4: serve the base model with the selected adapter, connect the
harness llm agent to it (smallest change), and test it with 1 task. Then
start the same eval runs as in Step 2 and the valid loss measurement in the
background with a log file and a macOS notification at the end. Update
train/STATE.md. Commit. Then stop. Do not poll.
```
## E2 — Report (after the notification)
```
Read CLAUDE.md, docs/stage1-training-task.md and train/STATE.md. Read only
the result files and the last 50 lines of the log. Write
runs/stage1/report.md as described in the task. Update train/STATE.md.
Commit. Show me the report. Then stop.
```

View File

@@ -0,0 +1,101 @@
# Task: Stage 1 training (ABAP corpus) on the Mac mini
## Context
Read CLAUDE.md first. This task adds stage 1 of the training plan:
1. Baseline: measure the base model on the eval tasks.
2. Stage 1: LoRA training of the base model on the ABAP corpus (continued
pretraining). The model learns ABAP, CDS, RAP and DDIC syntax.
3. Measure again with the trained model.
Stage 2 (SFT on teacher trajectories) is not part of this task.
Input: `~/projects/abap-llm/corpus/corpus.jsonl` (about 1,765 documents,
about 1.4M tokens, estimate). One record per line:
`{"text", "objects", "types", "language_version", "package_path",
"grouped", "obsolete", "tokens"}`. Token counts are estimates (chars / 4).
## Rules
- All CLAUDE.md rules apply.
- Training needs the memory of the Mac mini. During training, A4H must be
stopped and no other model may be loaded (also not in Ollama). Do not stop
or start A4H yourself. Tell me when I must stop or start it, and wait.
- Ask me before each long run (more than 30 minutes). Give the time estimate.
- Do not poll long runs. Start them in the background with a log file and a
macOS notification at the end, then stop and wait for my message. When I
write, read only the last lines of the log.
- Use the eval pool only for measurement. Never put eval tasks or their
solutions into training data.
- Put new code in `train/` in the harness repo. Keep changes to existing
harness code small. Commit after each step.
## Step 0: Setup
- Install `mlx-lm` in a separate virtual environment `train/.venv`.
- Base model: the model that the harness uses for the local llm agent (Qwen
27B). Find the exact model id in the harness config. Use a 4-bit MLX
version of the same model. If none exists, convert it with
`mlx_lm.convert` and 4-bit quantization. Record the model id and the
quantization in `train/README.md`.
- Check the options of `mlx_lm.lora` with `--help`. Do not guess options.
## Step 1: Data preparation (`train/prepare.py`)
- Count tokens again with the tokenizer of the base model. Replace the
estimates.
- Remove documents longer than `max_seq_length` (default 16384) and list
them in the report.
- Split by document, with a fixed seed: 95% train, 5% valid. A group is one
document, so a group is never in both sets.
- Write `train/data/train.jsonl` and `train/data/valid.jsonl` in the format
that `mlx_lm.lora` expects for plain text (field `text`). Keep the header
lines in the text.
- Report: document count, token count with the real tokenizer, token
distribution, removed documents.
## Step 2: Baseline (A4H must run)
- Run the base model on all tasks in the eval pool that are accepted
(T01, T13, T14, T15 and the accepted generated eval tasks). Use the
existing harness commands. One task at a time.
- Measure the base model loss on `valid.jsonl` (`mlx_lm.lora --test`
without adapter, or the equivalent option).
- Save the results in `runs/stage1/baseline.json`.
## Step 3: Training (A4H must be stopped)
- First a short test: 20 iterations. Check memory use and time per
iteration. If memory is not sufficient at 16384, tell me and suggest
options (for example max_seq_length 8192, fewer LoRA layers). Do not
change it yourself.
- Start values (record them in `train/config.yaml`):
- LoRA rank 16, all layers if memory allows
- learning rate 5e-5, with warmup and cosine decay if the tool supports it
- batch size 1, gradient checkpointing on
- 2 epochs (iterations = train documents × 2)
- valid loss every 200 iterations, save the adapter every 200 iterations
- Give me the time estimate for the full run and wait for my approval.
- During the run: if valid loss goes up 3 times in a row, stop and keep the
adapter with the lowest valid loss.
## Step 4: Measure again (A4H must run)
- Serve the base model with the adapter (for example `mlx_lm.server` with
`--adapter-path`). If the harness llm agent cannot use this server, add
the smallest change that lets it use an OpenAI-compatible endpoint.
- Run the same eval tasks as in Step 2.
- Measure the loss on `valid.jsonl` with the adapter.
## Report (`runs/stage1/report.md`, show it in the chat)
- Model id, quantization, LoRA settings, iterations, training time, peak
memory
- Valid loss: base model and adapter
- Eval results for each task: base model and adapter, score and gate
results
- Tool use: did the model still call the MCP tools correctly? Count tool
calls with format errors, base model and adapter.
- Your conclusion in 3 sentences: did stage 1 help, and is replay data
necessary (it is necessary if tool use or format got worse)?

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 85.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"discipline": 10.0,
"total": 85.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "10/10",
"tool_calls": 16,
"seconds": 146.4,
"run_dir": "runs/emp/14000_G0143_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players >= 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member = abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total * c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players < 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' AND iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member = abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total * c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players < 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member <> abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total * c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players < 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member = abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total / c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players < 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member = abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total * c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 3 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,24 @@
{
"id": "G0143",
"pool": "eval",
"object_type": "FUNC",
"category": "E",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -1,97 +0,0 @@
CLASS {{p}}crane_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS call
IMPORTING iv_type TYPE string
iv_hours TYPE decfloat34
iv_km TYPE i
iv_weekend TYPE abap_bool
iv_customer TYPE string
EXPORTING ev_base TYPE decfloat34
ev_travel TYPE decfloat34
ev_surcharge TYPE decfloat34
ev_discount TYPE decfloat34
ev_total TYPE decfloat34
ev_subrc TYPE sysubrc.
METHODS small_crane_base FOR TESTING.
METHODS invalid_crane_type FOR TESTING.
METHODS invalid_hours FOR TESTING.
METHODS travel_fee_cap FOR TESTING.
METHODS weekend_surcharge FOR TESTING.
METHODS gold_discount FOR TESTING.
METHODS rounding_order FOR TESTING.
METHODS total_calculation FOR TESTING.
ENDCLASS.
CLASS {{p}}crane_hidden IMPLEMENTATION.
METHOD call.
CALL FUNCTION '{{P}}CRANE_FEE_OLD'
EXPORTING
iv_crane_type = iv_type
iv_hours = iv_hours
iv_distance_km = iv_km
iv_weekend = iv_weekend
iv_customer = iv_customer
IMPORTING
ev_base_fee = ev_base
ev_travel_fee = ev_travel
ev_surcharge = ev_surcharge
ev_discount = ev_discount
ev_total = ev_total
EXCEPTIONS
invalid_crane_type = 1
invalid_hours = 2
OTHERS = 3.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD small_crane_base.
call( EXPORTING iv_type = 'SM' iv_hours = 2 iv_km = 0 iv_weekend = abap_false iv_customer = 'STD'
IMPORTING ev_base = DATA(lv_base) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 230 ) act = lv_base ).
ENDMETHOD.
METHOD invalid_crane_type.
call( EXPORTING iv_type = 'XX' iv_hours = 1 iv_km = 0 iv_weekend = abap_false iv_customer = 'STD'
IMPORTING ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
ENDMETHOD.
METHOD invalid_hours.
call( EXPORTING iv_type = 'SM' iv_hours = 0 iv_km = 0 iv_weekend = abap_false iv_customer = 'STD'
IMPORTING ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ).
ENDMETHOD.
METHOD travel_fee_cap.
call( EXPORTING iv_type = 'SM' iv_hours = 1 iv_km = 200 iv_weekend = abap_false iv_customer = 'STD'
IMPORTING ev_travel = DATA(lv_travel) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 125 ) act = lv_travel ).
ENDMETHOD.
METHOD weekend_surcharge.
call( EXPORTING iv_type = 'MD' iv_hours = 2 iv_km = 0 iv_weekend = abap_true iv_customer = 'STD'
IMPORTING ev_surcharge = DATA(lv_surcharge) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '92.50' ) act = lv_surcharge ).
ENDMETHOD.
METHOD gold_discount.
call( EXPORTING iv_type = 'LG' iv_hours = 1 iv_km = 10 iv_weekend = abap_false iv_customer = 'GOLD'
IMPORTING ev_discount = DATA(lv_discount) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '36.85' ) act = lv_discount ).
ENDMETHOD.
METHOD rounding_order.
call( EXPORTING iv_type = 'SM' iv_hours = CONV decfloat34( '0.87' ) iv_km = 0 iv_weekend = abap_false iv_customer = 'GOLD'
IMPORTING ev_discount = DATA(lv_discount) ev_total = DATA(lv_total) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '10.01' ) act = lv_discount ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '90.05' ) act = lv_total ).
ENDMETHOD.
METHOD total_calculation.
call( EXPORTING iv_type = 'MD' iv_hours = 3 iv_km = 20 iv_weekend = abap_true iv_customer = 'GOLD'
IMPORTING ev_total = DATA(lv_total) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '655.88' ) act = lv_total ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,151 @@
CLASS {{p}}t_bowl_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS fee_for
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE decfloat34.
METHODS lane_fee_basic FOR TESTING.
METHODS rounds_up_part_hour FOR TESTING.
METHODS peak_surcharge FOR TESTING.
METHODS no_peak_outside FOR TESTING.
METHODS weekend_surcharge FOR TESTING.
METHODS shoes_charge FOR TESTING.
METHODS member_discount FOR TESTING.
METHODS no_discount_below_min FOR TESTING.
METHODS invalid_input_raises FOR TESTING.
METHODS combined_charges FOR TESTING.
ENDCLASS.
CLASS {{p}}t_bowl_hidden IMPLEMENTATION.
METHOD fee_for.
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING
iv_lanes = iv_lanes
iv_minutes = iv_minutes
iv_players = iv_players
iv_day = iv_day
iv_start = iv_start
iv_member = iv_member
IMPORTING
ev_fee = rv_fee
EXCEPTIONS
invalid_input = 1
OTHERS = 2.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD lane_fee_basic.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD rounds_up_part_hour.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '6.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 30 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 31 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD peak_surcharge.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '14.40' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '180000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD no_peak_outside.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '175959' iv_member = abap_false ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '220000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD weekend_surcharge.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '13.80' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'SAT' iv_start = '140000' iv_member = abap_false ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '13.80' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'SUN' iv_start = '140000' iv_member = abap_false ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'MON' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD shoes_charge.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '19.50' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 3
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD member_discount.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '30.60' )
act = fee_for( iv_lanes = 2 iv_minutes = 60 iv_players = 4
iv_day = 'WED' iv_start = '140000' iv_member = abap_true ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '34.00' )
act = fee_for( iv_lanes = 2 iv_minutes = 60 iv_players = 4
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD no_discount_below_min.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '17.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 2
iv_day = 'WED' iv_start = '140000' iv_member = abap_true ) ).
ENDMETHOD.
METHOD invalid_input_raises.
DATA lv_fee TYPE decfloat34.
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING iv_lanes = 0 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false
IMPORTING ev_fee = lv_fee
EXCEPTIONS invalid_input = 1 OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING iv_lanes = 1 iv_minutes = 0 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false
IMPORTING ev_fee = lv_fee
EXCEPTIONS invalid_input = 1 OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING iv_lanes = 1 iv_minutes = 60 iv_players = -1
iv_day = 'WED' iv_start = '140000' iv_member = abap_false
IMPORTING ev_fee = lv_fee
EXCEPTIONS invalid_input = 1 OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
ENDMETHOD.
METHOD combined_charges.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '19.40' )
act = fee_for( iv_lanes = 1 iv_minutes = 45 iv_players = 2
iv_day = 'SUN' iv_start = '200000' iv_member = abap_true ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,54 +1,60 @@
{
"task": "T_CRANE",
"task": "G0143",
"mutants": [
{
"object": "{{P}}CRANE_FEE_OLD",
"mutant": "weekend surcharge 25 -> 20",
"object": "{{P}}BOWL_FEE_OLD",
"mutant": "line 24: < -> >= (rel)",
"status": "killed",
"hidden": "6/8",
"hidden": "0/10",
"failed_tests": [
"WEEKEND_SURCHARGE",
"TOTAL_CALCULATION"
"COMBINED_CHARGES",
"INVALID_INPUT_RAISES",
"LANE_FEE_BASIC",
"MEMBER_DISCOUNT",
"NO_DISCOUNT_BELOW_MIN",
"NO_PEAK_OUTSIDE",
"PEAK_SURCHARGE",
"ROUNDS_UP_PART_HOUR",
"SHOES_CHARGE",
"WEEKEND_SURCHARGE"
]
},
{
"object": "{{P}}CRANE_FEE_OLD",
"mutant": "gold discount 10 -> 5",
"object": "{{P}}BOWL_FEE_OLD",
"mutant": "line 35: OR -> AND (logic)",
"status": "killed",
"hidden": "5/8",
"hidden": "9/10",
"failed_tests": [
"GOLD_DISCOUNT",
"ROUNDING_ORDER",
"TOTAL_CALCULATION"
"WEEKEND_SURCHARGE"
]
},
{
"object": "{{P}}CRANE_FEE_OLD",
"mutant": "travel cap 100 -> 50",
"object": "{{P}}BOWL_FEE_OLD",
"mutant": "line 41: = -> <> (eq)",
"status": "killed",
"hidden": "7/8",
"hidden": "8/10",
"failed_tests": [
"TRAVEL_FEE_CAP"
"COMBINED_CHARGES",
"MEMBER_DISCOUNT"
]
},
{
"object": "{{P}}CRANE_FEE_OLD",
"mutant": "round half up -> half down",
"object": "{{P}}BOWL_FEE_OLD",
"mutant": "line 42: * -> / (arith)",
"status": "killed",
"hidden": "6/8",
"hidden": "8/10",
"failed_tests": [
"ROUNDING_ORDER",
"TOTAL_CALCULATION"
"COMBINED_CHARGES",
"MEMBER_DISCOUNT"
]
},
{
"object": "{{P}}CRANE_FEE_OLD",
"mutant": "SM hourly 115 -> 110",
"object": "{{P}}BOWL_FEE_OLD",
"mutant": "line 45: 2 -> 3 (const)",
"status": "killed",
"hidden": "6/8",
"hidden": "9/10",
"failed_tests": [
"SMALL_CRANE_BASE",
"ROUNDING_ORDER"
"COMBINED_CHARGES"
]
}
],

View File

@@ -0,0 +1,47 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
CONSTANTS c_half_hour_minutes TYPE i VALUE 30.
CONSTANTS c_lane_price TYPE decfloat34 VALUE '6.00'.
CONSTANTS c_shoe_price TYPE decfloat34 VALUE '2.50'.
CONSTANTS c_peak_start TYPE t VALUE '180000'.
CONSTANTS c_peak_end TYPE t VALUE '220000'.
CONSTANTS c_peak_factor TYPE decfloat34 VALUE '1.2'.
CONSTANTS c_weekend_factor TYPE decfloat34 VALUE '1.15'.
CONSTANTS c_member_factor TYPE decfloat34 VALUE '0.9'.
CONSTANTS c_member_min_fee TYPE decfloat34 VALUE '20.00'.
IF iv_lanes < 1 OR iv_minutes < 1 OR iv_players < 0.
RAISE invalid_input.
ENDIF.
DATA(lv_half_hours) = ( iv_minutes + c_half_hour_minutes - 1 ) DIV c_half_hour_minutes.
DATA(lv_lane_fee) = iv_lanes * lv_half_hours * c_lane_price.
IF iv_start >= c_peak_start AND iv_start < c_peak_end.
lv_lane_fee = lv_lane_fee * c_peak_factor.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_lane_fee = lv_lane_fee * c_weekend_factor.
ENDIF.
DATA(lv_total) = lv_lane_fee + iv_players * c_shoe_price.
IF iv_member = abap_true AND lv_total >= c_member_min_fee.
lv_total = lv_total * c_member_factor.
ENDIF.
ev_fee = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -1,71 +0,0 @@
CLASS {{p}}crane_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS fee_for
IMPORTING iv_type TYPE string
iv_hours TYPE decfloat34
iv_km TYPE i
iv_weekend TYPE abap_bool
iv_customer TYPE string
RETURNING VALUE(rv_total) TYPE decfloat34.
METHODS small_crane FOR TESTING.
METHODS weekend_and_gold FOR TESTING.
METHODS travel_cap FOR TESTING.
METHODS invalid_input FOR TESTING.
ENDCLASS.
CLASS {{p}}crane_test IMPLEMENTATION.
METHOD fee_for.
CALL FUNCTION '{{P}}CRANE_FEE_OLD'
EXPORTING
iv_crane_type = iv_type
iv_hours = iv_hours
iv_distance_km = iv_km
iv_weekend = iv_weekend
iv_customer = iv_customer
IMPORTING
ev_total = rv_total
EXCEPTIONS
invalid_crane_type = 1
invalid_hours = 2
OTHERS = 3.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD small_crane.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( 230 )
act = fee_for( iv_type = 'SM' iv_hours = 2 iv_km = 0 iv_weekend = abap_false iv_customer = 'STD' ) ).
ENDMETHOD.
METHOD weekend_and_gold.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '655.88' )
act = fee_for( iv_type = 'MD' iv_hours = 3 iv_km = 20 iv_weekend = abap_true iv_customer = 'GOLD' ) ).
ENDMETHOD.
METHOD travel_cap.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( 240 )
act = fee_for( iv_type = 'SM' iv_hours = 1 iv_km = 200 iv_weekend = abap_false iv_customer = 'STD' ) ).
ENDMETHOD.
METHOD invalid_input.
DATA lv_total TYPE decfloat34.
CALL FUNCTION '{{P}}CRANE_FEE_OLD'
EXPORTING
iv_crane_type = 'XX'
iv_hours = 1
iv_distance_km = 0
iv_weekend = abap_false
iv_customer = 'STD'
IMPORTING
ev_total = lv_total
EXCEPTIONS
invalid_crane_type = 1
invalid_hours = 2
OTHERS = 3.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,72 @@
CLASS {{p}}t_bowl_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS fee_for
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE decfloat34.
METHODS lane_fee FOR TESTING.
METHODS surcharges FOR TESTING.
METHODS member_discount FOR TESTING.
METHODS invalid_input FOR TESTING.
ENDCLASS.
CLASS {{p}}t_bowl_test IMPLEMENTATION.
METHOD fee_for.
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING
iv_lanes = iv_lanes
iv_minutes = iv_minutes
iv_players = iv_players
iv_day = iv_day
iv_start = iv_start
iv_member = iv_member
IMPORTING
ev_fee = rv_fee
EXCEPTIONS
invalid_input = 1
OTHERS = 2.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD lane_fee.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '12.00' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD surcharges.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '14.40' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '190000' iv_member = abap_false ) ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '13.80' )
act = fee_for( iv_lanes = 1 iv_minutes = 60 iv_players = 0
iv_day = 'SAT' iv_start = '140000' iv_member = abap_false ) ).
ENDMETHOD.
METHOD member_discount.
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '30.60' )
act = fee_for( iv_lanes = 2 iv_minutes = 60 iv_players = 4
iv_day = 'WED' iv_start = '140000' iv_member = abap_true ) ).
ENDMETHOD.
METHOD invalid_input.
DATA lv_fee TYPE decfloat34.
CALL FUNCTION '{{P}}BOWL_FEE_OLD'
EXPORTING iv_lanes = 0 iv_minutes = 60 iv_players = 0
iv_day = 'WED' iv_start = '140000' iv_member = abap_false
IMPORTING ev_fee = lv_fee
EXCEPTIONS invalid_input = 1 OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,67 @@
FUNCTION {{p}}bowl_fee_old
IMPORTING
iv_lanes TYPE i
iv_minutes TYPE i
iv_players TYPE i
iv_day TYPE string
iv_start TYPE t
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
EXCEPTIONS
invalid_input.
DATA: lv_a TYPE decfloat34,
lv_b TYPE decfloat34,
lv_c TYPE decfloat34,
lv_slots TYPE i,
lv_day TYPE string,
lv_shoe TYPE decfloat34 VALUE '2.5'.
IF iv_lanes < 1.
RAISE invalid_input.
ELSE.
IF iv_minutes < 1.
RAISE invalid_input.
ELSE.
IF iv_players < 0.
RAISE invalid_input.
ENDIF.
ENDIF.
ENDIF.
lv_slots = iv_minutes / 30.
IF lv_slots * 30 < iv_minutes.
lv_slots = lv_slots + 1.
ENDIF.
lv_a = iv_lanes * lv_slots * 6.
IF iv_start >= '180000'.
IF iv_start < '220000'.
lv_a = lv_a * 12 / 10.
ENDIF.
ENDIF.
lv_day = iv_day.
IF lv_day = 'SAT'.
lv_a = lv_a * 115 / 100.
ELSE.
IF lv_day = 'SUN'.
lv_a = lv_a * 115 / 100.
ENDIF.
ENDIF.
lv_b = iv_players * lv_shoe.
lv_c = lv_a + lv_b.
IF iv_member = 'X'.
IF lv_c >= 20.
lv_c = lv_c * 9 / 10.
ENDIF.
ENDIF.
ev_fee = round( val = lv_c dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -0,0 +1,17 @@
FUNCTION {{p}}bowl_info
IMPORTING
iv_day TYPE string
EXPORTING
ev_open TYPE t
ev_close TYPE t.
CASE iv_day.
WHEN 'SUN'.
ev_open = '100000'.
ev_close = '200000'.
WHEN OTHERS.
ev_open = '140000'.
ev_close = '230000'.
ENDCASE.
ENDFUNCTION.

View File

@@ -1,71 +0,0 @@
FUNCTION {{p}}crane_fee_old
IMPORTING
iv_crane_type TYPE string
iv_hours TYPE decfloat34
iv_distance_km TYPE i
iv_weekend TYPE abap_bool
iv_customer TYPE string
EXPORTING
ev_base_fee TYPE decfloat34
ev_travel_fee TYPE decfloat34
ev_surcharge TYPE decfloat34
ev_discount TYPE decfloat34
ev_total TYPE decfloat34
EXCEPTIONS
invalid_crane_type
invalid_hours.
DATA lv_hourly TYPE decfloat34.
DATA lv_per_km TYPE decfloat34.
DATA lv_km TYPE i.
DATA lv_sum TYPE decfloat34.
IF iv_hours <= 0.
RAISE invalid_hours.
ENDIF.
CASE iv_crane_type.
WHEN 'SM'.
lv_hourly = 115.
lv_per_km = '1.25'.
WHEN 'MD'.
lv_hourly = 185.
lv_per_km = '1.75'.
WHEN 'LG'.
lv_hourly = 345.
lv_per_km = '2.35'.
WHEN OTHERS.
RAISE invalid_crane_type.
ENDCASE.
ev_base_fee = iv_hours * lv_hourly.
lv_km = iv_distance_km.
IF lv_km > 100.
lv_km = 100.
ENDIF.
ev_travel_fee = lv_km * lv_per_km.
IF iv_weekend = abap_true.
ev_surcharge = ev_base_fee * 25 / 100.
ELSE.
ev_surcharge = 0.
ENDIF.
lv_sum = ev_base_fee + ev_travel_fee + ev_surcharge.
IF iv_customer = 'GOLD'.
ev_discount = lv_sum * 10 / 100.
ELSE.
ev_discount = 0.
ENDIF.
ev_total = lv_sum - ev_discount.
ev_base_fee = round( val = ev_base_fee dec = 2 mode = cl_abap_math=>round_half_up ).
ev_travel_fee = round( val = ev_travel_fee dec = 2 mode = cl_abap_math=>round_half_up ).
ev_surcharge = round( val = ev_surcharge dec = 2 mode = cl_abap_math=>round_half_up ).
ev_discount = round( val = ev_discount dec = 2 mode = cl_abap_math=>round_half_up ).
ev_total = round( val = ev_total dec = 2 mode = cl_abap_math=>round_half_up ).
ENDFUNCTION.

View File

@@ -1,48 +1,53 @@
# 1. Goal
Refactor the legacy function module {{P}}CRANE_FEE_OLD in function group
{{P}}FG_CRANE. The function module calculates the fee of a crane hire job. The
refactoring must improve the quality of the code. The behavior stays the same.
The name and the public interface stay the same.
Refactor the legacy function module {{P}}BOWL_FEE_OLD in function group
{{P}}FG_BOWL. The function module calculates the fee of a booking of bowling
lanes at a bowling centre. The booking program calls the function module for
each booking. The code is old, hard to read, and hard to maintain. Refactor the
code in place. Read the legacy code. The legacy code contains the calculation
rules. The behaviour stays the same.
# 2. Open questions
None.
# 3. Context
- Function group {{P}}FG_CRANE exists in package $TMP.
- Function module {{P}}CRANE_FEE_OLD exists in this group. It is the legacy
implementation of the fee calculation. Read its source. The source defines
the fee rules of the crane hire company.
- A crane hire company uses the function module to price jobs. The dispatcher
enters the crane type, the number of hours, the distance in kilometres,
whether the job is on a weekend, and the customer category. The function
module returns the base fee, the travel fee, the surcharge, the discount, and
the total fee.
- Function group {{P}}FG_BOWL exists in package $TMP.
- Function module {{P}}BOWL_FEE_OLD exists in this group. It contains the
legacy code.
- The booking program calls {{P}}BOWL_FEE_OLD. The booking program also calls
{{P}}BOWL_INFO.
# 4. Contract
- Refactor the function module {{P}}CRANE_FEE_OLD in function group
{{P}}FG_CRANE in place.
- Keep the name and the public interface exactly as they are.
- The interface is:
- Importing: iv_crane_type TYPE string, iv_hours TYPE decfloat34,
iv_distance_km TYPE i, iv_weekend TYPE abap_bool, iv_customer TYPE string.
- Exporting: ev_base_fee TYPE decfloat34, ev_travel_fee TYPE decfloat34,
ev_surcharge TYPE decfloat34, ev_discount TYPE decfloat34,
ev_total TYPE decfloat34.
- Exceptions: invalid_crane_type, invalid_hours.
- Refactor the function module {{P}}BOWL_FEE_OLD in function group
{{P}}FG_BOWL.
- The name, the parameter names, the parameter kinds, the parameter types, and
the exceptions stay exactly as they are:
- IMPORTING iv_lanes TYPE i
- IMPORTING iv_minutes TYPE i
- IMPORTING iv_players TYPE i
- IMPORTING iv_day TYPE string
- IMPORTING iv_start TYPE t
- IMPORTING iv_member TYPE abap_bool
- EXPORTING ev_fee TYPE decfloat34
- EXCEPTIONS invalid_input
# 5. Business rules
The behavior of the current implementation is the specification. The
refactored function module must return exactly the same values as the current
implementation for every input. Read the legacy source carefully to learn the
rules. Do not change the behavior.
1. The legacy code defines the calculation. Read the legacy code. The result
stays the same for every input combination.
2. The code follows Clean ABAP: small units with one task, no deep nesting, no
magic numbers, and meaningful names.
3. No unit has more than 40 statements.
4. The code has no syntax error and no ATC finding of priority 1 or 2.
# 6. Constraints
- Release target: 7.58.
- Coding standards: Clean ABAP. Methods must be shorter than 40 statements.
Pass large parameters by reference.
- Out of scope: do not change the function group {{P}}FG_CRANE.
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
- Package $TMP. Do not use transports.
- The refactored code stays in the body of {{P}}BOWL_FEE_OLD, between the
FUNCTION statement and the ENDFUNCTION statement. Do not add local classes
after ENDFUNCTION; the source tooling does not activate them.
- Out of scope: do not change {{P}}BOWL_INFO. Do not change the interface of
{{P}}BOWL_FEE_OLD.
# 7. Acceptance
- The function module {{P}}CRANE_FEE_OLD is active and has no syntax error.
- The function module {{P}}BOWL_FEE_OLD is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests for the function module in a global test class.

View File

@@ -3,7 +3,7 @@
"category": "E",
"object_type": "FUNC",
"difficulty": 2,
"release_target": "v758",
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 60,
@@ -12,83 +12,85 @@
"seed": [
{
"type": "FUGR",
"name": "{{P}}FG_CRANE",
"description": "Crane hire function group"
"name": "{{P}}FG_BOWL",
"description": "Bowling centre fee function group"
},
{
"type": "FUNC",
"name": "{{P}}CRANE_FEE_OLD",
"functionGroup": "{{P}}FG_CRANE",
"file": "seed/crane_fee_old.func.abap",
"description": "Legacy crane fee calculation"
"name": "{{P}}BOWL_FEE_OLD",
"functionGroup": "{{P}}FG_BOWL",
"file": "seed/bowl_fee_old.func.abap",
"description": "Legacy bowling lane booking fee calculation"
},
{
"type": "FUNC",
"name": "{{P}}BOWL_INFO",
"functionGroup": "{{P}}FG_BOWL",
"file": "seed/bowl_info.func.abap",
"description": "Opening hours of the bowling centre, out of scope"
}
],
"contract": [
{
"type": "FUNC",
"name": "{{P}}CRANE_FEE_OLD",
"functionGroup": "{{P}}FG_CRANE",
"name": "{{P}}BOWL_FEE_OLD",
"functionGroup": "{{P}}FG_BOWL",
"params": [
{
"name": "iv_crane_type",
"type": "string"
},
{
"name": "iv_hours",
"type": "decfloat34"
},
{
"name": "iv_distance_km",
"name": "iv_lanes",
"type": "i"
},
{
"name": "iv_weekend",
"type": "abap_bool"
"name": "iv_minutes",
"type": "i"
},
{
"name": "iv_customer",
"name": "iv_players",
"type": "i"
},
{
"name": "iv_day",
"type": "string"
},
{
"name": "ev_base_fee",
"type": "decfloat34"
"name": "iv_start",
"type": "t"
},
{
"name": "ev_travel_fee",
"type": "decfloat34"
"name": "iv_member",
"type": "abap_bool"
},
{
"name": "ev_surcharge",
"type": "decfloat34"
},
{
"name": "ev_discount",
"type": "decfloat34"
},
{
"name": "ev_total",
"name": "ev_fee",
"type": "decfloat34"
}
]
}
],
"out_of_scope": [
"{{P}}FG_CRANE"
"{{P}}BOWL_INFO"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}CRANE_HIDDEN",
"file": "hidden/crane_hidden.clas.abap",
"description": "Hidden behaviour tests for the crane fee"
"name": "{{P}}T_BOWL_HIDDEN",
"file": "hidden/t_bowl_hidden.clas.abap",
"description": "Hidden tests for the bowling fee behaviour"
}
],
"reference": [
{
"type": "FUNC",
"name": "{{P}}BOWL_FEE_OLD",
"functionGroup": "{{P}}FG_BOWL",
"file": "reference/bowl_fee_old.func.abap",
"description": "Refactored bowling fee function module"
},
{
"type": "CLAS",
"name": "{{P}}CRANE_TEST",
"file": "reference/crane_test.clas.abap",
"description": "Own tests for the crane fee"
"name": "{{P}}T_BOWL_TEST",
"file": "reference/t_bowl_test.clas.abap",
"description": "Own tests for the bowling fee function module"
}
],
"craft_checks": [

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 80.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"discipline": 5.0,
"total": 80.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 30,
"seconds": 214.6,
"run_dir": "runs/emp/14001_G0144_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 5 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total - c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 4 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day <> 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total - c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 4 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_false.
lv_total = lv_total - c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 4 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total + c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 4 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total - c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size < 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,80 @@
{
"id": "G0144",
"pool": "eval",
"object_type": "FUNC",
"category": "E",
"attempts": [
{
"stage": "bundle",
"errors": [
"reference/t14_test.clas.abap line 33: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"reference/t14_test.clas.abap line 39: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"reference/t14_test.clas.abap line 45: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"reference/t14_test.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 40: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 47: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 54: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 66: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 72: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 78: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 81: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 105: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 108: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 111: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\"",
"hidden/t14_hidden.clas.abap line 117: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"call\""
],
"by_check": {
"abaplint": 20
}
},
{
"stage": "bundle",
"errors": [
"reference/t14_test.clas.abap line 39: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"reference/t14_test.clas.abap line 45: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"reference/t14_test.clas.abap line 51: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"reference/t14_test.clas.abap line 57: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 46: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 53: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 60: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 63: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 69: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 72: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 78: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 84: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 87: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 93: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 99: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 105: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 111: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 114: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 117: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\"",
"hidden/t14_hidden.clas.abap line 123: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"run_case\""
],
"by_check": {
"abaplint": 20
}
},
{
"stage": "validate",
"oracle": null,
"null": null
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,118 @@
CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES: BEGIN OF ty_result,
fee TYPE decfloat34,
note TYPE string,
subrc TYPE sysubrc,
END OF ty_result.
METHODS calc
IMPORTING iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
RETURNING VALUE(rs_result) TYPE ty_result.
METHODS adult_base FOR TESTING.
METHODS child_base FOR TESTING.
METHODS senior_student_base FOR TESTING.
METHODS family_flat FOR TESTING.
METHODS family_extra FOR TESTING.
METHODS weekend_surcharge FOR TESTING.
METHODS member_discount FOR TESTING.
METHODS member_not_below_zero FOR TESTING.
METHODS group_discount FOR TESTING.
METHODS invalid_inputs FOR TESTING.
METHODS note_text FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_hidden IMPLEMENTATION.
METHOD calc.
rs_result-fee = 0.
rs_result-note = ''.
CALL FUNCTION '{{P}}MUSEUM_FEE_OLD'
EXPORTING
iv_visitor = iv_visitor
iv_day = iv_day
iv_size = iv_size
iv_member = iv_member
IMPORTING
ev_fee = rs_result-fee
ev_note = rs_result-note
EXCEPTIONS
invalid_input = 1
OTHERS = 2.
rs_result-subrc = sy-subrc.
ENDMETHOD.
METHOD adult_base.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 2 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_r-subrc ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 24 ) act = ls_r-fee ).
ENDMETHOD.
METHOD child_base.
DATA(ls_r) = me->calc( iv_visitor = 'CHILD' iv_day = 'MON' iv_size = 3 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_r-subrc ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 18 ) act = ls_r-fee ).
ENDMETHOD.
METHOD senior_student_base.
DATA(ls_r) = me->calc( iv_visitor = 'SENIOR' iv_day = 'MON' iv_size = 1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 8 ) act = ls_r-fee ).
DATA(ls_s) = me->calc( iv_visitor = 'STUDENT' iv_day = 'MON' iv_size = 2 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 14 ) act = ls_s-fee ).
ENDMETHOD.
METHOD family_flat.
DATA(ls_r) = me->calc( iv_visitor = 'FAMILY' iv_day = 'MON' iv_size = 4 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 30 ) act = ls_r-fee ).
DATA(ls_s) = me->calc( iv_visitor = 'FAMILY' iv_day = 'MON' iv_size = 2 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 30 ) act = ls_s-fee ).
ENDMETHOD.
METHOD family_extra.
DATA(ls_r) = me->calc( iv_visitor = 'FAMILY' iv_day = 'MON' iv_size = 6 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 42 ) act = ls_r-fee ).
ENDMETHOD.
METHOD weekend_surcharge.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'SAT' iv_size = 1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '14.4' ) act = ls_r-fee ).
DATA(ls_s) = me->calc( iv_visitor = 'ADULT' iv_day = 'SUN' iv_size = 1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '14.4' ) act = ls_s-fee ).
ENDMETHOD.
METHOD member_discount.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 2 iv_member = abap_true ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 14 ) act = ls_r-fee ).
ENDMETHOD.
METHOD member_not_below_zero.
DATA(ls_r) = me->calc( iv_visitor = 'CHILD' iv_day = 'MON' iv_size = 1 iv_member = abap_true ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = ls_r-fee ).
ENDMETHOD.
METHOD group_discount.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 10 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 108 ) act = ls_r-fee ).
ENDMETHOD.
METHOD invalid_inputs.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 0 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_r-subrc ).
DATA(ls_s) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = -1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_s-subrc ).
DATA(ls_t) = me->calc( iv_visitor = 'XXX' iv_day = 'MON' iv_size = 1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_t-subrc ).
ENDMETHOD.
METHOD note_text.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 1 iv_member = abap_true ).
cl_abap_unit_assert=>assert_equals( exp = 'Adult member' act = ls_r-note ).
DATA(ls_s) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 10 iv_member = abap_true ).
cl_abap_unit_assert=>assert_equals( exp = 'Adult member group' act = ls_s-note ).
DATA(ls_t) = me->calc( iv_visitor = 'CHILD' iv_day = 'SAT' iv_size = 1 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 'Child' act = ls_t-note ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,102 +0,0 @@
CLASS {{p}}t_ski_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS call
IMPORTING iv_equipment TYPE string
iv_days TYPE i
iv_customer TYPE string
iv_insurance TYPE abap_bool
EXPORTING ev_price TYPE decfloat34
ev_deposit TYPE decfloat34
ev_subrc TYPE sysubrc.
METHODS invalid_days FOR TESTING.
METHODS invalid_equipment FOR TESTING.
METHODS invalid_customer FOR TESTING.
METHODS adult_ski_one_day FOR TESTING.
METHODS child_boots_two_days FOR TESTING.
METHODS senior_snowboard_3d FOR TESTING.
METHODS adult_ski_7d_insurance FOR TESTING.
METHODS child_helmet_10d_ins FOR TESTING.
METHODS rounding_case FOR TESTING.
ENDCLASS.
CLASS {{p}}t_ski_hidden IMPLEMENTATION.
METHOD call.
CALL FUNCTION '{{P}}SKI_RENTAL_OLD'
EXPORTING
iv_equipment = iv_equipment
iv_days = iv_days
iv_customer = iv_customer
iv_insurance = iv_insurance
IMPORTING
ev_price = ev_price
ev_deposit = ev_deposit
EXCEPTIONS
invalid_equipment = 1
invalid_days = 2
invalid_customer = 3
OTHERS = 4.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD invalid_days.
call( EXPORTING iv_equipment = 'SKI' iv_days = 0 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ).
ENDMETHOD.
METHOD invalid_equipment.
call( EXPORTING iv_equipment = 'X' iv_days = 1 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ).
ENDMETHOD.
METHOD invalid_customer.
call( EXPORTING iv_equipment = 'SKI' iv_days = 1 iv_customer = 'X' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ).
ENDMETHOD.
METHOD adult_ski_one_day.
call( EXPORTING iv_equipment = 'SKI' iv_days = 1 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_deposit ).
ENDMETHOD.
METHOD child_boots_two_days.
call( EXPORTING iv_equipment = 'BOOTS' iv_days = 2 iv_customer = 'CHILD' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 20 ) act = lv_deposit ).
ENDMETHOD.
METHOD senior_snowboard_3d.
call( EXPORTING iv_equipment = 'SNOWBOARD' iv_days = 3 iv_customer = 'SENIOR' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '68.40' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_deposit ).
ENDMETHOD.
METHOD adult_ski_7d_insurance.
call( EXPORTING iv_equipment = 'SKI' iv_days = 7 iv_customer = 'ADULT' iv_insurance = abap_true
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '192.50' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = lv_deposit ).
ENDMETHOD.
METHOD child_helmet_10d_ins.
call( EXPORTING iv_equipment = 'HELMET' iv_days = 10 iv_customer = 'CHILD' iv_insurance = abap_true
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '72.50' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 60 ) act = lv_deposit ).
ENDMETHOD.
METHOD rounding_case.
call( EXPORTING iv_equipment = 'SKI' iv_days = 3 iv_customer = 'CHILD' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.63' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_deposit ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
{
"task": "G0144",
"mutants": [
{
"object": "{{P}}MUSEUM_FEE_OLD",
"mutant": "line 49: 4 -> 5 (const)",
"status": "killed",
"hidden": "10/11",
"failed_tests": [
"FAMILY_EXTRA"
]
},
{
"object": "{{P}}MUSEUM_FEE_OLD",
"mutant": "line 55: = -> <> (eq)",
"status": "killed",
"hidden": "3/11",
"failed_tests": [
"ADULT_BASE",
"CHILD_BASE",
"FAMILY_EXTRA",
"FAMILY_FLAT",
"GROUP_DISCOUNT",
"MEMBER_DISCOUNT",
"SENIOR_STUDENT_BASE",
"WEEKEND_SURCHARGE"
]
},
{
"object": "{{P}}MUSEUM_FEE_OLD",
"mutant": "line 59: abap_true -> abap_false (bool)",
"status": "killed",
"hidden": "2/11",
"failed_tests": [
"ADULT_BASE",
"CHILD_BASE",
"FAMILY_EXTRA",
"FAMILY_FLAT",
"GROUP_DISCOUNT",
"MEMBER_DISCOUNT",
"MEMBER_NOT_BELOW_ZERO",
"SENIOR_STUDENT_BASE",
"WEEKEND_SURCHARGE"
]
},
{
"object": "{{P}}MUSEUM_FEE_OLD",
"mutant": "line 60: - -> + (arith)",
"status": "killed",
"hidden": "9/11",
"failed_tests": [
"MEMBER_DISCOUNT",
"MEMBER_NOT_BELOW_ZERO"
]
},
{
"object": "{{P}}MUSEUM_FEE_OLD",
"mutant": "line 66: >= -> < (rel)",
"status": "killed",
"hidden": "3/11",
"failed_tests": [
"ADULT_BASE",
"CHILD_BASE",
"FAMILY_EXTRA",
"FAMILY_FLAT",
"GROUP_DISCOUNT",
"MEMBER_DISCOUNT",
"SENIOR_STUDENT_BASE",
"WEEKEND_SURCHARGE"
]
}
],
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,91 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
CONSTANTS:
c_adult TYPE decfloat34 VALUE '12.00',
c_child TYPE decfloat34 VALUE '6.00',
c_senior TYPE decfloat34 VALUE '8.00',
c_student TYPE decfloat34 VALUE '7.00',
c_family TYPE decfloat34 VALUE '30.00',
c_extra TYPE decfloat34 VALUE '6.00',
c_member TYPE decfloat34 VALUE '10.00',
c_weekend TYPE decfloat34 VALUE '1.20',
c_group TYPE decfloat34 VALUE '0.90'.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
CASE iv_visitor.
WHEN 'ADULT'.
lv_base = c_adult.
WHEN 'CHILD'.
lv_base = c_child.
WHEN 'SENIOR'.
lv_base = c_senior.
WHEN 'STUDENT'.
lv_base = c_student.
WHEN 'FAMILY'.
lv_base = c_family.
WHEN OTHERS.
RAISE invalid_input.
ENDCASE.
IF iv_visitor = 'FAMILY'.
lv_total = lv_base.
IF iv_size > 4.
lv_total = lv_total + ( iv_size - 4 ) * c_extra.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT' OR iv_day = 'SUN'.
lv_total = lv_total * c_weekend.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total - c_member.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * c_group.
ENDIF.
ev_fee = lv_total.
ev_note = ''.
CASE iv_visitor.
WHEN 'ADULT'.
ev_note = 'Adult'.
WHEN 'CHILD'.
ev_note = 'Child'.
WHEN 'SENIOR'.
ev_note = 'Senior'.
WHEN 'STUDENT'.
ev_note = 'Student'.
WHEN 'FAMILY'.
ev_note = 'Family'.
ENDCASE.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -1,166 +0,0 @@
FUNCTION {{p}}ski_rental_old
IMPORTING
iv_equipment TYPE string
iv_days TYPE i
iv_customer TYPE string
iv_insurance TYPE abap_bool
EXPORTING
ev_price TYPE decfloat34
ev_deposit TYPE decfloat34
EXCEPTIONS
invalid_equipment
invalid_days
invalid_customer.
CLASS lcx_invalid_equipment DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS lcx_invalid_equipment IMPLEMENTATION.
ENDCLASS.
CLASS lcx_invalid_days DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS lcx_invalid_days IMPLEMENTATION.
ENDCLASS.
CLASS lcx_invalid_customer DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS lcx_invalid_customer IMPLEMENTATION.
ENDCLASS.
CLASS lcl_ski_pricer DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_result,
price TYPE decfloat34,
deposit TYPE decfloat34,
END OF ty_result.
CLASS-METHODS calculate
IMPORTING iv_equipment TYPE string
iv_days TYPE i
iv_customer TYPE string
iv_insurance TYPE abap_bool
RETURNING VALUE(rs_result) TYPE ty_result
RAISING lcx_invalid_equipment lcx_invalid_days lcx_invalid_customer.
PRIVATE SECTION.
CLASS-METHODS get_daily_rate
IMPORTING iv_equipment TYPE string
RETURNING VALUE(rv_rate) TYPE decfloat34
RAISING lcx_invalid_equipment.
CLASS-METHODS get_deposit
IMPORTING iv_equipment TYPE string
RETURNING VALUE(rv_deposit) TYPE decfloat34
RAISING lcx_invalid_equipment.
CLASS-METHODS get_customer_discount
IMPORTING iv_customer TYPE string
RETURNING VALUE(rv_discount) TYPE decfloat34
RAISING lcx_invalid_customer.
CLASS-METHODS get_duration_discount
IMPORTING iv_days TYPE i
RETURNING VALUE(rv_discount) TYPE decfloat34.
ENDCLASS.
CLASS lcl_ski_pricer IMPLEMENTATION.
METHOD calculate.
CONSTANTS: c_insurance_per_day TYPE decfloat34 VALUE 5,
c_insurance_deposit TYPE decfloat34 VALUE 50.
IF iv_days < 1.
RAISE EXCEPTION TYPE lcx_invalid_days.
ENDIF.
DATA(lv_daily_rate) = get_daily_rate( iv_equipment ).
DATA(lv_customer_disc) = get_customer_discount( iv_customer ).
DATA(lv_total) = lv_daily_rate * iv_days.
lv_total = lv_total - lv_total * lv_customer_disc / 100.
DATA(lv_duration_disc) = get_duration_discount( iv_days ).
lv_total = lv_total - lv_total * lv_duration_disc / 100.
IF iv_insurance = abap_true.
lv_total = lv_total + c_insurance_per_day * iv_days.
ENDIF.
rs_result-price = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
rs_result-deposit = get_deposit( iv_equipment ).
IF iv_insurance = abap_true.
rs_result-deposit = rs_result-deposit + c_insurance_deposit.
ENDIF.
ENDMETHOD.
METHOD get_daily_rate.
CONSTANTS: c_ski_rate TYPE decfloat34 VALUE 25,
c_snowboard_rate TYPE decfloat34 VALUE 30,
c_boots_rate TYPE decfloat34 VALUE 10,
c_helmet_rate TYPE decfloat34 VALUE 5.
CASE iv_equipment.
WHEN 'SKI'.
rv_rate = c_ski_rate.
WHEN 'SNOWBOARD'.
rv_rate = c_snowboard_rate.
WHEN 'BOOTS'.
rv_rate = c_boots_rate.
WHEN 'HELMET'.
rv_rate = c_helmet_rate.
WHEN OTHERS.
RAISE EXCEPTION TYPE lcx_invalid_equipment.
ENDCASE.
ENDMETHOD.
METHOD get_deposit.
CONSTANTS: c_ski_deposit TYPE decfloat34 VALUE 100,
c_snowboard_deposit TYPE decfloat34 VALUE 120,
c_boots_deposit TYPE decfloat34 VALUE 20,
c_helmet_deposit TYPE decfloat34 VALUE 10.
CASE iv_equipment.
WHEN 'SKI'.
rv_deposit = c_ski_deposit.
WHEN 'SNOWBOARD'.
rv_deposit = c_snowboard_deposit.
WHEN 'BOOTS'.
rv_deposit = c_boots_deposit.
WHEN 'HELMET'.
rv_deposit = c_helmet_deposit.
WHEN OTHERS.
RAISE EXCEPTION TYPE lcx_invalid_equipment.
ENDCASE.
ENDMETHOD.
METHOD get_customer_discount.
CONSTANTS: c_child_discount TYPE decfloat34 VALUE 50,
c_senior_discount TYPE decfloat34 VALUE 20.
CASE iv_customer.
WHEN 'ADULT'.
rv_discount = 0.
WHEN 'CHILD'.
rv_discount = c_child_discount.
WHEN 'SENIOR'.
rv_discount = c_senior_discount.
WHEN OTHERS.
RAISE EXCEPTION TYPE lcx_invalid_customer.
ENDCASE.
ENDMETHOD.
METHOD get_duration_discount.
CONSTANTS: c_disc_7 TYPE decfloat34 VALUE 10,
c_disc_3 TYPE decfloat34 VALUE 5.
IF iv_days >= 7.
rv_discount = c_disc_7.
ELSEIF iv_days >= 3.
rv_discount = c_disc_3.
ELSE.
rv_discount = 0.
ENDIF.
ENDMETHOD.
ENDCLASS.
TRY.
DATA(ls_result) = lcl_ski_pricer=>calculate(
iv_equipment = iv_equipment
iv_days = iv_days
iv_customer = iv_customer
iv_insurance = iv_insurance ).
ev_price = ls_result-price.
ev_deposit = ls_result-deposit.
CATCH lcx_invalid_equipment.
RAISE invalid_equipment.
CATCH lcx_invalid_days.
RAISE invalid_days.
CATCH lcx_invalid_customer.
RAISE invalid_customer.
ENDTRY.
ENDFUNCTION.

View File

@@ -0,0 +1,60 @@
CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES: BEGIN OF ty_result,
fee TYPE decfloat34,
note TYPE string,
subrc TYPE sysubrc,
END OF ty_result.
METHODS calc
IMPORTING iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
RETURNING VALUE(rs_result) TYPE ty_result.
METHODS adult_fee FOR TESTING.
METHODS family_fee FOR TESTING.
METHODS weekend_and_member FOR TESTING.
METHODS invalid_input FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_test IMPLEMENTATION.
METHOD calc.
rs_result-fee = 0.
rs_result-note = ''.
CALL FUNCTION '{{P}}MUSEUM_FEE_OLD'
EXPORTING
iv_visitor = iv_visitor
iv_day = iv_day
iv_size = iv_size
iv_member = iv_member
IMPORTING
ev_fee = rs_result-fee
ev_note = rs_result-note
EXCEPTIONS
invalid_input = 1
OTHERS = 2.
rs_result-subrc = sy-subrc.
ENDMETHOD.
METHOD adult_fee.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 2 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 24 ) act = ls_r-fee ).
ENDMETHOD.
METHOD family_fee.
DATA(ls_r) = me->calc( iv_visitor = 'FAMILY' iv_day = 'MON' iv_size = 6 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 42 ) act = ls_r-fee ).
ENDMETHOD.
METHOD weekend_and_member.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'SAT' iv_size = 2 iv_member = abap_true ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '18.8' ) act = ls_r-fee ).
ENDMETHOD.
METHOD invalid_input.
DATA(ls_r) = me->calc( iv_visitor = 'ADULT' iv_day = 'MON' iv_size = 0 iv_member = abap_false ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_r-subrc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,94 +0,0 @@
CLASS {{p}}t_ski_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS call
IMPORTING iv_equipment TYPE string
iv_days TYPE i
iv_customer TYPE string
iv_insurance TYPE abap_bool
EXPORTING ev_price TYPE decfloat34
ev_deposit TYPE decfloat34
ev_subrc TYPE sysubrc.
METHODS test_invalid_days FOR TESTING.
METHODS test_invalid_equipment FOR TESTING.
METHODS test_invalid_customer FOR TESTING.
METHODS test_adult_ski FOR TESTING.
METHODS test_child_boots FOR TESTING.
METHODS test_senior_snowboard FOR TESTING.
METHODS test_insurance FOR TESTING.
METHODS test_rounding FOR TESTING.
ENDCLASS.
CLASS {{p}}t_ski_test IMPLEMENTATION.
METHOD call.
CALL FUNCTION '{{P}}SKI_RENTAL_OLD'
EXPORTING
iv_equipment = iv_equipment
iv_days = iv_days
iv_customer = iv_customer
iv_insurance = iv_insurance
IMPORTING
ev_price = ev_price
ev_deposit = ev_deposit
EXCEPTIONS
invalid_equipment = 1
invalid_days = 2
invalid_customer = 3
OTHERS = 4.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD test_invalid_days.
call( EXPORTING iv_equipment = 'SKI' iv_days = 0 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_subrc ).
ENDMETHOD.
METHOD test_invalid_equipment.
call( EXPORTING iv_equipment = 'X' iv_days = 1 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_subrc ).
ENDMETHOD.
METHOD test_invalid_customer.
call( EXPORTING iv_equipment = 'SKI' iv_days = 1 iv_customer = 'X' iv_insurance = abap_false
IMPORTING ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_subrc ).
ENDMETHOD.
METHOD test_adult_ski.
call( EXPORTING iv_equipment = 'SKI' iv_days = 1 iv_customer = 'ADULT' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_deposit ).
ENDMETHOD.
METHOD test_child_boots.
call( EXPORTING iv_equipment = 'BOOTS' iv_days = 2 iv_customer = 'CHILD' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 20 ) act = lv_deposit ).
ENDMETHOD.
METHOD test_senior_snowboard.
call( EXPORTING iv_equipment = 'SNOWBOARD' iv_days = 3 iv_customer = 'SENIOR' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '68.40' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = lv_deposit ).
ENDMETHOD.
METHOD test_insurance.
call( EXPORTING iv_equipment = 'SKI' iv_days = 7 iv_customer = 'ADULT' iv_insurance = abap_true
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '192.50' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = lv_deposit ).
ENDMETHOD.
METHOD test_rounding.
call( EXPORTING iv_equipment = 'SKI' iv_days = 3 iv_customer = 'CHILD' iv_insurance = abap_false
IMPORTING ev_price = DATA(lv_price) ev_deposit = DATA(lv_deposit) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '35.63' ) act = lv_price ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_deposit ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,82 @@
FUNCTION {{p}}museum_fee_old
IMPORTING
iv_visitor TYPE string
iv_day TYPE string
iv_size TYPE i
iv_member TYPE abap_bool
EXPORTING
ev_fee TYPE decfloat34
ev_note TYPE string
EXCEPTIONS
invalid_input.
DATA lv_base TYPE decfloat34.
DATA lv_total TYPE decfloat34.
IF iv_size <= 0.
RAISE invalid_input.
ENDIF.
IF iv_visitor = 'ADULT'.
lv_base = 12.
ELSEIF iv_visitor = 'CHILD'.
lv_base = 6.
ELSEIF iv_visitor = 'SENIOR'.
lv_base = 8.
ELSEIF iv_visitor = 'STUDENT'.
lv_base = 7.
ELSEIF iv_visitor = 'FAMILY'.
lv_base = 30.
ELSE.
RAISE invalid_input.
ENDIF.
IF iv_visitor = 'FAMILY'.
IF iv_size <= 4.
lv_total = 30.
ELSE.
lv_total = 30 + ( iv_size - 4 ) * 6.
ENDIF.
ELSE.
lv_total = lv_base * iv_size.
ENDIF.
IF iv_day = 'SAT'.
lv_total = lv_total * '1.2'.
ELSEIF iv_day = 'SUN'.
lv_total = lv_total * '1.2'.
ENDIF.
IF iv_member = abap_true.
lv_total = lv_total - 10.
IF lv_total < 0.
lv_total = 0.
ENDIF.
ENDIF.
IF iv_size >= 10.
lv_total = lv_total * '0.9'.
ENDIF.
ev_fee = lv_total.
IF iv_visitor = 'ADULT'.
ev_note = 'Adult'.
ELSEIF iv_visitor = 'CHILD'.
ev_note = 'Child'.
ELSEIF iv_visitor = 'SENIOR'.
ev_note = 'Senior'.
ELSEIF iv_visitor = 'STUDENT'.
ev_note = 'Student'.
ELSEIF iv_visitor = 'FAMILY'.
ev_note = 'Family'.
ENDIF.
IF iv_member = abap_true.
ev_note = ev_note && ' member'.
ENDIF.
IF iv_size >= 10.
ev_note = ev_note && ' group'.
ENDIF.
ENDFUNCTION.

View File

@@ -1,78 +0,0 @@
FUNCTION {{p}}ski_rental_old
IMPORTING
iv_equipment TYPE string
iv_days TYPE i
iv_customer TYPE string
iv_insurance TYPE abap_bool
EXPORTING
ev_price TYPE decfloat34
ev_deposit TYPE decfloat34
EXCEPTIONS
invalid_equipment
invalid_days
invalid_customer.
DATA lv_daily TYPE decfloat34.
DATA lv_discount TYPE decfloat34.
DATA lv_total TYPE decfloat34.
DATA lv_duration_disc TYPE decfloat34.
IF iv_days < 1.
RAISE invalid_days.
ENDIF.
IF iv_equipment = 'SKI'.
lv_daily = 25.
ELSEIF iv_equipment = 'SNOWBOARD'.
lv_daily = 30.
ELSEIF iv_equipment = 'BOOTS'.
lv_daily = 10.
ELSEIF iv_equipment = 'HELMET'.
lv_daily = 5.
ELSE.
RAISE invalid_equipment.
ENDIF.
IF iv_customer = 'ADULT'.
lv_discount = 0.
ELSEIF iv_customer = 'CHILD'.
lv_discount = 50.
ELSEIF iv_customer = 'SENIOR'.
lv_discount = 20.
ELSE.
RAISE invalid_customer.
ENDIF.
lv_total = lv_daily * iv_days.
lv_total = lv_total - lv_total * lv_discount / 100.
IF iv_days >= 7.
lv_duration_disc = 10.
ELSEIF iv_days >= 3.
lv_duration_disc = 5.
ELSE.
lv_duration_disc = 0.
ENDIF.
lv_total = lv_total - lv_total * lv_duration_disc / 100.
IF iv_insurance = abap_true.
lv_total = lv_total + 5 * iv_days.
ENDIF.
ev_price = round( val = lv_total dec = 2 mode = cl_abap_math=>round_half_up ).
IF iv_equipment = 'SKI'.
ev_deposit = 100.
ELSEIF iv_equipment = 'SNOWBOARD'.
ev_deposit = 120.
ELSEIF iv_equipment = 'BOOTS'.
ev_deposit = 20.
ELSEIF iv_equipment = 'HELMET'.
ev_deposit = 10.
ENDIF.
IF iv_insurance = abap_true.
ev_deposit = ev_deposit + 50.
ENDIF.
ENDFUNCTION.

View File

@@ -1,32 +1,41 @@
# 1. Goal
Refactor the legacy function module {{P}}SKI_RENTAL_OLD in function group {{P}}FG_SKI. The function module calculates the rental price and the deposit for ski equipment. Improve the readability, the maintainability, and the structure of the code. The behavior must stay the same. The public interface must not change.
Refactor the legacy function module {{P}}MUSEUM_FEE_OLD in function group
{{P}}FG_MUSEUM. The function module calculates the entry fee for a museum visit.
The behavior of the function module must stay the same. Improve the code
quality.
# 2. Open questions
None.
# 3. Context
- Function group {{P}}FG_SKI exists in package $TMP.
- Function module {{P}}SKI_RENTAL_OLD exists in this group. It contains legacy code.
- A rental program calls the function module. The callers use the current interface.
- Function group {{P}}FG_MUSEUM exists in package $TMP.
- Function module {{P}}MUSEUM_FEE_OLD exists in this group. It contains legacy
code.
- The function module is called by a ticketing program. The interface must not
change.
# 4. Contract
- The function module {{P}}SKI_RENTAL_OLD keeps its name and its function group.
- The interface stays exactly the same:
- Importing: iv_equipment TYPE string, iv_days TYPE i, iv_customer TYPE string, iv_insurance TYPE abap_bool
- Exporting: ev_price TYPE decfloat34, ev_deposit TYPE decfloat34
- Exceptions: invalid_equipment, invalid_days, invalid_customer
- Refactor the function module {{P}}MUSEUM_FEE_OLD in function group
{{P}}FG_MUSEUM.
- Keep the name and the public interface: the same parameter names, kinds,
types, and exceptions.
# 5. Business rules
The business rules are defined by the current implementation of {{P}}SKI_RENTAL_OLD. The behavior must stay the same. Read the legacy code to understand the rules.
1. The behavior of the function module must not change. The legacy code defines
the business rules.
2. The function module must raise the exception INVALID_INPUT for the same
inputs as the legacy code.
3. The exported values must be the same as the legacy code for all valid
inputs.
# 6. Constraints
- Release target: 8.16.
- The function module must be active and have no syntax error.
- Refactor the code in place. Do not change the public interface.
- Follow Clean ABAP: small methods, no duplication, meaningful names, constants for literals.
- Out of scope: do not change the function group {{P}}FG_SKI. Do not create new function modules.
- Coding standards: Clean ABAP, no ATC priority 1 or 2 findings, methods below
40 statements.
- Out of scope: none.
# 7. Acceptance
- The function module is active and has no syntax error.
- The behavior is unchanged.
- The hidden tests pass.
- Write ABAP Unit tests for the function module in a global test class.

View File

@@ -12,52 +12,47 @@
"seed": [
{
"type": "FUGR",
"name": "{{P}}FG_SKI",
"description": "Ski rental function group"
"name": "{{P}}FG_MUSEUM",
"description": "Museum fee function group"
},
{
"type": "FUNC",
"name": "{{P}}SKI_RENTAL_OLD",
"functionGroup": "{{P}}FG_SKI",
"file": "seed/ski_rental_old.func.abap",
"description": "Legacy ski rental pricing"
"name": "{{P}}MUSEUM_FEE_OLD",
"functionGroup": "{{P}}FG_MUSEUM",
"file": "seed/museum_fee_old.func.abap",
"description": "Legacy museum fee calculator"
}
],
"contract": [
{
"type": "FUNC",
"name": "{{P}}SKI_RENTAL_OLD",
"functionGroup": "{{P}}FG_SKI",
"name": "{{P}}MUSEUM_FEE_OLD",
"functionGroup": "{{P}}FG_MUSEUM",
"params": [
{
"name": "iv_equipment",
"name": "iv_visitor",
"type": "string"
},
{
"name": "iv_days",
"name": "iv_day",
"type": "string"
},
{
"name": "iv_size",
"type": "i"
},
{
"name": "iv_customer",
"type": "string"
},
{
"name": "iv_insurance",
"name": "iv_member",
"type": "abap_bool"
},
{
"name": "ev_price",
"name": "ev_fee",
"type": "decfloat34"
},
{
"name": "ev_deposit",
"type": "decfloat34"
"name": "ev_note",
"type": "string"
}
],
"exceptions": [
"invalid_equipment",
"invalid_days",
"invalid_customer"
]
}
],
@@ -65,24 +60,24 @@
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T_SKI_HIDDEN",
"file": "hidden/t_ski_hidden.clas.abap",
"description": "Hidden tests for ski rental pricing"
"name": "{{P}}T14_HIDDEN",
"file": "hidden/t14_hidden.clas.abap",
"description": "T14 hidden tests"
}
],
"reference": [
{
"type": "FUNC",
"name": "{{P}}SKI_RENTAL_OLD",
"functionGroup": "{{P}}FG_SKI",
"file": "reference/ski_rental_old.func.abap",
"description": "Refactored ski rental pricing"
"name": "{{P}}MUSEUM_FEE_OLD",
"functionGroup": "{{P}}FG_MUSEUM",
"file": "reference/museum_fee_old.func.abap",
"description": "Refactored museum fee calculator"
},
{
"type": "CLAS",
"name": "{{P}}T_SKI_TEST",
"file": "reference/t_ski_test.clas.abap",
"description": "Own tests for ski rental pricing"
"name": "{{P}}T14_TEST",
"file": "reference/t14_test.clas.abap",
"description": "T14 own tests"
}
],
"craft_checks": [

View File

@@ -1,5 +1,6 @@
{
"deepseek-v4.1-flash:cloud": {
"_superseded": {
"deepseek-v4.1-flash:cloud@2026-10-03": {
"score": 0,
"parts": {
"total": 0,
@@ -14,3 +15,4 @@
"run_dir": "runs/emp/12014_G0174_llm_deepseek-v4.1-flash_cloud"
}
}
}

View File

@@ -8,6 +8,13 @@
"stage": "validate",
"oracle": 100,
"null": 30
},
{
"stage": "manual_fix",
"by": "Claude review 2026-10-03 (Kral decision)",
"change": "Sort direction added (highest first). New gap: rule 4 ignores small sales without a definition of a small sale.",
"oracle": 100,
"null": 30
}
],
"accepted": true

View File

@@ -1,9 +1,7 @@
{
"decision": "flag",
"note": "Gap is only the missing sort direction of TOTAL_REVENUE. A model can guess 'highest first'; is it critical enough for a stop task?",
"decision": "accept",
"note": "Kral decision: sort direction added; new gap = undefined 'small sale'. Oracle 100 (keywords), null 30.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {
"9": "zayif"
}
"failed_checks": {}
}

View File

@@ -18,7 +18,9 @@ None.
# 5. Business rules
1. Include a sale only if SALE_DATE is on or after P_FROM and FLOWER_TYPE is in S_FLOWER.
2. Show one line for each flower type that has at least one included sale.
3. Sort the lines by TOTAL_REVENUE.
3. Sort the lines by TOTAL_REVENUE, the highest revenue first.
4. Ignore small sales. A small sale does not count for TOTAL_REVENUE and not
for COUNT.
# 6. Constraints
- Release target: 816.

View File

@@ -6,19 +6,12 @@
"release_target": "v816",
"expected_outcome": "stop",
"expected_gap": {
"description": "The sort direction for TOTAL_REVENUE is missing.",
"description": "Rule 4 says that small sales are ignored, but the spec does not define when a sale is small (no amount limit is given).",
"keywords": [
[
"sort",
"order"
],
[
"sort",
"direction"
],
[
"ascending",
"descending"
"small",
"sale",
"defin"
]
]
},

146
train/subset.json Normal file
View File

@@ -0,0 +1,146 @@
{
"name": "stage1-subset",
"created": "2026-10-03",
"seed": 20261003,
"quota": {
"A": 3,
"B": 3,
"C": 3,
"E": 3,
"F": 3,
"D": 2,
"G": 2,
"H": 2,
"I": 2,
"K": 2
},
"rule": "accepted and review=accept; per category a fixed quota; object types spread; T01 added (old Qwen baseline). Use this list unchanged before and after training.",
"tasks": [
{
"id": "T01",
"category": "A",
"object_type": "CLAS",
"note": "hand-written; old Qwen run 41.7"
},
{
"id": "G0105",
"category": "A",
"object_type": "FUNC"
},
{
"id": "G0104",
"category": "A",
"object_type": "CLAS"
},
{
"id": "G0112",
"category": "B",
"object_type": "PROG"
},
{
"id": "G0017",
"category": "B",
"object_type": "DDLS"
},
{
"id": "G0003",
"category": "B",
"object_type": "CLAS"
},
{
"id": "G0123",
"category": "C",
"object_type": "FUNC"
},
{
"id": "G0125",
"category": "C",
"object_type": "PROG"
},
{
"id": "G0010",
"category": "C",
"object_type": "CLAS"
},
{
"id": "G0139",
"category": "E",
"object_type": "CLAS"
},
{
"id": "G0142",
"category": "E",
"object_type": "PROG"
},
{
"id": "G0147",
"category": "E",
"object_type": "DDLS"
},
{
"id": "G0154",
"category": "F",
"object_type": "DDLS"
},
{
"id": "G0007",
"category": "F",
"object_type": "CLAS"
},
{
"id": "G0151",
"category": "F",
"object_type": "FUNC"
},
{
"id": "G0128",
"category": "D",
"object_type": "CLAS"
},
{
"id": "G0133",
"category": "D",
"object_type": "FUNC"
},
{
"id": "G0157",
"category": "G",
"object_type": "FUNC"
},
{
"id": "G0162",
"category": "G",
"object_type": "PROG"
},
{
"id": "G0170",
"category": "H",
"object_type": "CLAS"
},
{
"id": "G0174",
"category": "H",
"object_type": "PROG"
},
{
"id": "G0166",
"category": "I",
"object_type": "DDLS"
},
{
"id": "G0167",
"category": "I",
"object_type": "CLAS"
},
{
"id": "G0185",
"category": "K",
"object_type": "FUNC"
},
{
"id": "G0181",
"category": "K",
"object_type": "PROG"
}
]
}