STRU and exception tasks accepted; G6 cascade fix, exception class mutants, RTTI unit notes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-05 19:25:09 +02:00
parent b119f1afac
commit 55f4068330
36 changed files with 5380 additions and 34 deletions

View File

@@ -74,7 +74,7 @@ needs an amount or a quantity, the reference field needs a reference annotation
abap.cuky in the same table; @Semantics.quantity.unitOfMeasure : '{{p}}ord.unit' with unit : abap.unit(3)); a
reference without the table name fails with "Annotation with reference to unit code ... is uncomplete". Reference: the DDL file and ONE global own-test class
(type CLAS). Hidden tests (one global class): RTTI on the table: cast cl_abap_typedescr=>describe_by_name( '{{P}}ORD' ) to
cl_abap_structdescr and check get_ddic_field_list( ) (field names, key flags, lengths, decimals, types), and a
cl_abap_structdescr and check get_ddic_field_list( ) (field names, key flags, LENG in characters, decimals, types; cl_abap_elemdescr->length is in BYTES: 2 per character), and a
cl_osql_test_environment test (create( i_dependency_list = VALUE #( ( '{{P}}ORD' ) ) ), insert, select back; a second
INSERT with the same key gives sy-subrc = 4). No seed is needed.""",
"STRU": """The model must create a DDIC STRUCTURE (type STRU) as the main contract object. DDL source: annotations
@@ -83,7 +83,9 @@ INSERT with the same key gives sy-subrc = 4). No seed is needed.""",
"include {{p}}other;" when the spec asks). Contract entry: {"type": "STRU", "name": "{{P}}NAME", "fields": [...]}
(the field names in lower case). The Business rules list every field with data type and length. Reference: the DDL file
and ONE global own-test class (type CLAS). Hidden tests (one global class): RTTI: cl_abap_typedescr=>describe_by_name(
'{{P}}NAME' ) cast to cl_abap_structdescr; check components (names, length, decimals, type kind) and the order.""",
'{{P}}NAME' ) cast to cl_abap_structdescr; check components (names, length, decimals, type kind) and the order. RTTI units: cl_abap_elemdescr->length is in BYTES
(2 bytes per character in Unicode), so a char(10) field has length 20; compare characters with output_length or with
LENG of cl_abap_structdescr=>get_ddic_field_list( ) (characters), and decimals with ->decimals.""",
"MSAG": """The model must create a MESSAGE CLASS (type MSAG) as the main contract object. The name has at most 20
characters including the prefix. A message class has NO source file. In task.json the reference entry has no "file":
{"type": "MSAG", "name": "{{P}}MSG", "description": "...", "messages": [{"msgno": "001", "text": "Order &1 is blocked"}, ...]}

View File

@@ -169,6 +169,8 @@ def decl_mutants(src, otype, seed, n=MAX_MUTANTS):
for m in re.finditer(r"^(\s*)key(\s+)(?!client\b)(\w+\s*:)", src, re.I | re.M):
add("key", m, f"{m.group(1)}{m.group(3)}")
if otype == "INTF":
for m in re.finditer(r"(\bmsgno\s*=\s*')(\d+)(')", src, re.I): # message number of an exception text id
add("msgno", m, f"{m.group(1)}{str(int(m.group(2)) + 1).zfill(len(m.group(2)))}{m.group(3)}")
for m in re.finditer(r"(\bVALUE\s+)(\d+)(?=\s*\.)", src, re.I):
add("const", m, f"{m.group(1)}{int(m.group(2)) + 1}")
for m in re.finditer(r"(\bVALUE\s+)'([^'\n]{1,20})'", src, re.I):
@@ -236,7 +238,10 @@ def check_task(pool_root, task_id, run_base, n=MAX_MUTANTS, keep=False):
src = open(os.path.join(task_dir, o["file"])).read()
if o["type"] in ("TABL", "STRU", "INTF"):
return decl_mutants(src, o["type"], f"{task_id}:{o['name']}", n)
return mutants(src, o["type"], f"{task_id}:{o['name']}", n)
ms = mutants(src, o["type"], f"{task_id}:{o['name']}", n)
if o["type"] == "CLAS" and len(ms) < n: # exception classes: constants, message numbers, texts
ms += decl_mutants(src, "INTF", f"{task_id}:{o['name']}", n - len(ms))
return ms
cand = [[(o, d, m) for d, m in _muts(o)] for o in targets]
plan = []
while len(plan) < n and any(cand):

View File

@@ -426,8 +426,15 @@ class Runner:
contract_files = {f"{c['name'].lower()}.{c['type'].lower()}.abap" for c in contract}
# abaplint does not know standard superclasses (CX_STATIC_CHECK ...); an exception hierarchy
# then gives "Super class ... not found or contains errors". A4H activation (G1) covers this.
# Cascade: a class that uses such an exception class gets "Definition for X not found in scope" (X is
# the exception class abaplint could not build). Messages that name a broken class are not counted.
broken = {re.sub(r"\.(clas|intf)\.abap$", "", i["file"]).upper() for i in issues
if re.match(r"Super class .* not found or contains errors", i["msg"] or "")}
def _cascade(msg):
return any(re.search(rf"\b{re.escape(b)}\b", msg or "", re.I) for b in broken)
g["G6_release"] = not any(i["rule"] == "check_syntax" and i["file"] in contract_files
and not re.match(r"Super class .* not found or contains errors", i["msg"] or "")
and not _cascade(i["msg"])
for i in issues)
# 8 score

View File

@@ -0,0 +1,105 @@
REPORT {{p}}oil_report.
DATA gv_region TYPE {{p}}grove-region.
PARAMETERS p_from TYPE d OBLIGATORY.
SELECT-OPTIONS s_reg FOR gv_region.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_line,
region TYPE {{p}}grove-region,
litres TYPE {{p}}press-litres,
kilos TYPE {{p}}press-kilos,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY.
TYPES tt_region TYPE RANGE OF {{p}}grove-region.
METHODS collect
IMPORTING iv_from TYPE d
it_region TYPE tt_region
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS show
CHANGING ct_lines TYPE tt_line.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD collect.
SELECT gr~region,
SUM( pr~litres ) AS litres,
SUM( pr~kilos ) AS kilos
FROM {{p}}press AS pr
INNER JOIN {{p}}grove AS gr ON gr~grove_id = pr~grove_id
WHERE pr~press_date < @iv_from
AND gr~region IN @it_region
GROUP BY gr~region
ORDER BY litres DESCENDING
INTO CORRESPONDING FIELDS OF TABLE @rt_lines.
ENDMETHOD.
METHOD show.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->collect( iv_from = p_from it_region = s_reg[] ).
go_report->show( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS litres_per_region FOR TESTING.
METHODS region_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}GROVE' ) ( '{{P}}PRESS' ) ) ).
DATA lt_grove TYPE STANDARD TABLE OF {{p}}grove WITH EMPTY KEY.
lt_grove = VALUE #( ( grove_id = 1 grove_name = 'A' region = 'NORTH' variety = 'ARBEQ' )
( grove_id = 2 grove_name = 'B' region = 'NORTH' variety = 'ARBEQ' )
( grove_id = 3 grove_name = 'C' region = 'SOUTH' variety = 'PICUA' ) ).
go_env->insert_test_data( lt_grove ).
DATA lt_press TYPE STANDARD TABLE OF {{p}}press WITH EMPTY KEY.
lt_press = VALUE #( ( press_id = 1 grove_id = 1 press_date = '20260110' kilos = 1000 litres = '200.00' )
( press_id = 2 grove_id = 2 press_date = '20260111' kilos = 500 litres = '90.00' )
( press_id = 3 grove_id = 3 press_date = '20260112' kilos = 800 litres = '150.00' ) ).
go_env->insert_test_data( lt_press ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD litres_per_region.
DATA lv_expected TYPE {{p}}press-litres.
lv_expected = '290.00'.
DATA(lt_lines) = NEW lcl_report( )->collect(
iv_from = '20260101' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'NORTH' act = lt_lines[ 1 ]-region ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ).
cl_abap_unit_assert=>assert_equals( exp = 1500 act = lt_lines[ 1 ]-kilos ).
ENDMETHOD.
METHOD region_filter.
DATA lv_expected TYPE {{p}}press-litres.
lv_expected = '150.00'.
DATA(lt_lines) = NEW lcl_report( )->collect(
iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,105 @@
REPORT {{p}}oil_report.
DATA gv_region TYPE {{p}}grove-region.
PARAMETERS p_from TYPE d OBLIGATORY.
SELECT-OPTIONS s_reg FOR gv_region.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_line,
region TYPE {{p}}grove-region,
litres TYPE {{p}}press-litres,
kilos TYPE {{p}}press-kilos,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY.
TYPES tt_region TYPE RANGE OF {{p}}grove-region.
METHODS collect
IMPORTING iv_from TYPE d
it_region TYPE tt_region
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS show
CHANGING ct_lines TYPE tt_line.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD collect.
SELECT gr~region,
SUM( pr~litres ) AS litres,
SUM( pr~kilos ) AS kilos
FROM {{p}}press AS pr
INNER JOIN {{p}}grove AS gr ON gr~grove_id = pr~grove_id
WHERE pr~press_date >= @iv_from
OR gr~region IN @it_region
GROUP BY gr~region
ORDER BY litres DESCENDING
INTO CORRESPONDING FIELDS OF TABLE @rt_lines.
ENDMETHOD.
METHOD show.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->collect( iv_from = p_from it_region = s_reg[] ).
go_report->show( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS litres_per_region FOR TESTING.
METHODS region_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}GROVE' ) ( '{{P}}PRESS' ) ) ).
DATA lt_grove TYPE STANDARD TABLE OF {{p}}grove WITH EMPTY KEY.
lt_grove = VALUE #( ( grove_id = 1 grove_name = 'A' region = 'NORTH' variety = 'ARBEQ' )
( grove_id = 2 grove_name = 'B' region = 'NORTH' variety = 'ARBEQ' )
( grove_id = 3 grove_name = 'C' region = 'SOUTH' variety = 'PICUA' ) ).
go_env->insert_test_data( lt_grove ).
DATA lt_press TYPE STANDARD TABLE OF {{p}}press WITH EMPTY KEY.
lt_press = VALUE #( ( press_id = 1 grove_id = 1 press_date = '20260110' kilos = 1000 litres = '200.00' )
( press_id = 2 grove_id = 2 press_date = '20260111' kilos = 500 litres = '90.00' )
( press_id = 3 grove_id = 3 press_date = '20260112' kilos = 800 litres = '150.00' ) ).
go_env->insert_test_data( lt_press ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD litres_per_region.
DATA lv_expected TYPE {{p}}press-litres.
lv_expected = '290.00'.
DATA(lt_lines) = NEW lcl_report( )->collect(
iv_from = '20260101' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'NORTH' act = lt_lines[ 1 ]-region ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ).
cl_abap_unit_assert=>assert_equals( exp = 1500 act = lt_lines[ 1 ]-kilos ).
ENDMETHOD.
METHOD region_filter.
DATA lv_expected TYPE {{p}}press-litres.
lv_expected = '150.00'.
DATA(lt_lines) = NEW lcl_report( )->collect(
iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = lt_lines[ 1 ]-litres ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,34 @@
{
"id": "G1140",
"pool": "train",
"object_type": "PROG",
"category": "B",
"attempts": [
{
"stage": "bundle",
"errors": [
"Too close to task G0020 (similarity spec 0.67, rules 0.49, names 1.00). Choose a different business topic and different object names.",
"Too close to task G0183 (similarity spec 0.60, rules 0.00, names 1.00). Choose a different business topic and different object names."
],
"by_check": {
"extra": 2
}
},
{
"stage": "validate",
"oracle": 86.7,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 2,
"killed": 2,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -15,7 +15,7 @@ CLASS {{p}}mill_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
IMPORTING ir_data TYPE REF TO data
iv_row TYPE i
iv_col TYPE string
RETURNING VALUE(rv_value) TYPE string.
RETURNING VALUE(rv_value) TYPE {{p}}press-litres.
METHODS lines_of
IMPORTING ir_data TYPE REF TO data
RETURNING VALUE(rv_lines) TYPE i.
@@ -60,9 +60,12 @@ CLASS {{p}}mill_hidden IMPLEMENTATION.
ENDMETHOD.
METHOD amount_of.
DATA lv_amount TYPE p LENGTH 15 DECIMALS 2.
lv_amount = value_of( ir_data = ir_data iv_row = iv_row iv_col = iv_col ).
rv_value = CONV string( lv_amount ).
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
ASSIGN ir_data->* TO <lt_data>.
ASSIGN <lt_data>[ iv_row ] TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT iv_col OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_value>).
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ).
rv_value = <lv_value>.
ENDMETHOD.
METHOD all_regions.
@@ -70,29 +73,40 @@ CLASS {{p}}mill_hidden IMPLEMENTATION.
ENDMETHOD.
METHOD sorted_by_litres.
DATA(lr_data) = run( '20260101' ).
DATA lr_data TYPE REF TO data.
lr_data = run( '20260101' ).
cl_abap_unit_assert=>assert_equals( exp = `NORTH` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `SOUTH` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REGION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `WEST` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `REGION` ) ).
ENDMETHOD.
METHOD litres_and_kilos.
DATA(lr_data) = run( '20260101' ).
cl_abap_unit_assert=>assert_equals( exp = `290.00` act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1500` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `KILOS` ) ).
DATA lr_data TYPE REF TO data.
DATA lv_expected TYPE {{p}}press-litres.
lr_data = run( '20260101' ).
lv_expected = '290.00'.
cl_abap_unit_assert=>assert_equals( exp = lv_expected
act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1500`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `KILOS` ) ).
ENDMETHOD.
METHOD region_filter.
DATA(lr_data) = run( iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
DATA lr_data TYPE REF TO data.
DATA lv_expected TYPE {{p}}press-litres.
lr_data = run( iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
lv_expected = '150.00'.
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `150.00` act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected
act = amount_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD two_region_filter.
DATA(lr_data) = run( iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'WEST' )
( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
DATA lr_data TYPE REF TO data.
lr_data = run( iv_from = '20260101'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'WEST' )
( sign = 'I' option = 'EQ' low = 'SOUTH' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `SOUTH` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `WEST` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `REGION` ) ).

View File

@@ -1,5 +1,5 @@
{
"task": "T50",
"task": "G1140",
"mutants": [
{
"object": "{{P}}OIL_REPORT",
@@ -8,37 +8,29 @@
"hidden": "0/6",
"failed_tests": [
"ALL_REGIONS",
"SORTED_BY_LITRES",
"DATE_FILTER",
"LITRES_AND_KILOS",
"REGION_FILTER",
"TWO_REGION_FILTER",
"DATE_FILTER"
"SORTED_BY_LITRES",
"TWO_REGION_FILTER"
]
},
{
"object": "{{P}}OIL_REPORT",
"mutant": "line 34: AND -> OR (logic)",
"status": "killed",
"hidden": "3/6",
"hidden": "1/6",
"failed_tests": [
"ALL_REGIONS",
"DATE_FILTER",
"REGION_FILTER",
"TWO_REGION_FILTER",
"DATE_FILTER"
]
},
{
"object": "{{P}}OIL_REPORT",
"mutant": "line 36: DESCENDING -> ASCENDING (order)",
"status": "killed",
"hidden": "4/6",
"failed_tests": [
"SORTED_BY_LITRES",
"TWO_REGION_FILTER"
]
}
],
"valid": 3,
"killed": 3,
"valid": 2,
"killed": 2,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,6 @@
@EndUserText.label : 'Audit information'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}audit_info {
created_by : abap.char(11);
created_on : abap.dats;
}

View File

@@ -0,0 +1,6 @@
@EndUserText.label : 'Audit information'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}audit_info {
created_by : abap.numc(12);
created_on : abap.dats;
}

View File

@@ -0,0 +1,10 @@
@EndUserText.label : 'Stock position line'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}stock_line {
material : abap.char(18);
plant : abap.char(4);
net_amount : abap.dec(9,3);
currency : abap.char(5);
created_by : abap.char(12);
created_on : abap.dats;
}

View File

@@ -0,0 +1,6 @@
@EndUserText.label : 'Audit information'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}audit_info {
created_by : abap.char(12);
created_on : abap.tims;
}

View File

@@ -0,0 +1,29 @@
{
"id": "G1903",
"pool": "train",
"object_type": "STRU",
"category": "B",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 4,
"killed": 4,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,177 @@
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_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS six_components FOR TESTING.
METHODS component_order FOR TESTING.
METHODS char_lengths FOR TESTING.
METHODS amount_type FOR TESTING.
METHODS date_type FOR TESTING.
METHODS flat_structure FOR TESTING.
METHODS audit_structure FOR TESTING.
METHODS audit_matches FOR TESTING.
METHODS read_stock_row FOR TESTING.
ENDCLASS.
CLASS {{p}}t16_hidden IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}STOCK' ) ) ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD setup.
go_env->clear_doubles( ).
DATA lt_stock TYPE STANDARD TABLE OF {{p}}stock WITH EMPTY KEY.
lt_stock = VALUE #(
( material = 'MAT-1' plant = 'P001' net_amount = '123.45'
currency = 'EUR' created_by = 'USER1' created_on = '20240115' )
( material = 'MAT-2' plant = 'P001' net_amount = '10.00'
currency = 'EUR' created_by = 'USER2' created_on = '20240116' )
( material = 'MAT-3' plant = 'P002' net_amount = '5.50'
currency = 'USD' created_by = 'USER1' created_on = '20240117' ) ).
go_env->insert_test_data( lt_stock ).
ENDMETHOD.
METHOD six_components.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
cl_abap_unit_assert=>assert_equals( exp = 6 act = lines( lt_fields ) ).
ENDMETHOD.
METHOD component_order.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_comp) = lo_struct->get_components( ).
cl_abap_unit_assert=>assert_equals( exp = 6 act = lines( lt_comp ) ).
DATA(lv_index) = 0.
LOOP AT lt_comp INTO DATA(ls_comp).
lv_index = lv_index + 1.
CASE lv_index.
WHEN 1.
cl_abap_unit_assert=>assert_equals( exp = 'MATERIAL' act = ls_comp-name ).
WHEN 2.
cl_abap_unit_assert=>assert_equals( exp = 'PLANT' act = ls_comp-name ).
WHEN 3.
cl_abap_unit_assert=>assert_equals( exp = 'NET_AMOUNT' act = ls_comp-name ).
WHEN 4.
cl_abap_unit_assert=>assert_equals( exp = 'CURRENCY' act = ls_comp-name ).
WHEN 5.
cl_abap_unit_assert=>assert_equals( exp = 'CREATED_BY' act = ls_comp-name ).
WHEN 6.
cl_abap_unit_assert=>assert_equals( exp = 'CREATED_ON' act = ls_comp-name ).
ENDCASE.
ENDLOOP.
ENDMETHOD.
METHOD char_lengths.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
READ TABLE lt_fields INTO DATA(ls_field) WITH KEY fieldname = 'MATERIAL'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component MATERIAL is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 18 act = CONV i( ls_field-leng ) ).
READ TABLE lt_fields INTO ls_field WITH KEY fieldname = 'PLANT'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component PLANT is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = CONV i( ls_field-leng ) ).
READ TABLE lt_fields INTO ls_field WITH KEY fieldname = 'CURRENCY'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component CURRENCY is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = CONV i( ls_field-leng ) ).
READ TABLE lt_fields INTO ls_field WITH KEY fieldname = 'CREATED_BY'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component CREATED_BY is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 12 act = CONV i( ls_field-leng ) ).
ENDMETHOD.
METHOD amount_type.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
READ TABLE lt_fields INTO DATA(ls_field) WITH KEY fieldname = 'NET_AMOUNT'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component NET_AMOUNT is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 'DEC' act = ls_field-datatype ).
cl_abap_unit_assert=>assert_equals( exp = 9 act = CONV i( ls_field-leng ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = CONV i( ls_field-decimals ) ).
ENDMETHOD.
METHOD date_type.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
READ TABLE lt_fields INTO DATA(ls_field) WITH KEY fieldname = 'CREATED_ON'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component CREATED_ON is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 'DATS' act = ls_field-datatype ).
cl_abap_unit_assert=>assert_equals( exp = 8 act = CONV i( ls_field-leng ) ).
ENDMETHOD.
METHOD flat_structure.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_comp) = lo_struct->get_components( ).
LOOP AT lt_comp INTO DATA(ls_comp).
cl_abap_unit_assert=>assert_equals(
exp = cl_abap_typedescr=>kind_elem
act = ls_comp-type->kind
msg = |Component { ls_comp-name } is not elementary| ).
ENDLOOP.
ENDMETHOD.
METHOD audit_structure.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}AUDIT_INFO' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_fields ) ).
READ TABLE lt_fields INTO DATA(ls_field) WITH KEY fieldname = 'CREATED_BY'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component CREATED_BY is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 'CHAR' act = ls_field-datatype ).
cl_abap_unit_assert=>assert_equals( exp = 12 act = CONV i( ls_field-leng ) ).
READ TABLE lt_fields INTO ls_field WITH KEY fieldname = 'CREATED_ON'.
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = 'Component CREATED_ON is missing' ).
cl_abap_unit_assert=>assert_equals( exp = 'DATS' act = ls_field-datatype ).
cl_abap_unit_assert=>assert_equals( exp = 8 act = CONV i( ls_field-leng ) ).
ENDMETHOD.
METHOD audit_matches.
DATA(lo_line) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lo_audit) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}AUDIT_INFO' ) ).
DATA(lt_line) = lo_line->get_ddic_field_list( ).
DATA(lt_audit) = lo_audit->get_ddic_field_list( ).
LOOP AT lt_audit INTO DATA(ls_audit).
READ TABLE lt_line INTO DATA(ls_line) WITH KEY fieldname = ls_audit-fieldname.
cl_abap_unit_assert=>assert_subrc(
exp = 0 msg = |Component { ls_audit-fieldname } is missing| ).
cl_abap_unit_assert=>assert_equals( exp = ls_audit-leng act = ls_line-leng ).
cl_abap_unit_assert=>assert_equals( exp = ls_audit-datatype act = ls_line-datatype ).
ENDLOOP.
ENDMETHOD.
METHOD read_stock_row.
DATA ls_line TYPE {{p}}stock_line.
SELECT SINGLE material, plant, net_amount, currency, created_by, created_on
FROM {{p}}stock
INTO @ls_line
WHERE material = 'MAT-1' AND plant = 'P001'.
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = ls_line-material ).
cl_abap_unit_assert=>assert_equals( exp = 'P001' act = ls_line-plant ).
cl_abap_unit_assert=>assert_equals( exp = 'EUR' act = ls_line-currency ).
cl_abap_unit_assert=>assert_equals( exp = 'USER1' act = ls_line-created_by ).
cl_abap_unit_assert=>assert_equals(
exp = CONV d( '20240115' ) act = ls_line-created_on ).
cl_abap_unit_assert=>assert_equals(
exp = CONV decfloat34( '123.45' )
act = CONV decfloat34( ls_line-net_amount ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,55 @@
{
"task": "G1903",
"mutants": [
{
"object": "{{P}}AUDIT_INFO",
"mutant": "line 4: abap.char(12) -> abap.char(11) (length)",
"status": "killed",
"hidden": "7/9",
"failed_tests": [
"AUDIT_MATCHES",
"AUDIT_STRUCTURE"
]
},
{
"object": "{{P}}STOCK_LINE",
"mutant": "line 4: abap.char(18) -> abap.numc(18) (type)",
"status": "invalid",
"hidden": "0/0",
"failed_tests": []
},
{
"object": "{{P}}AUDIT_INFO",
"mutant": "line 4: abap.char(12) -> abap.numc(12) (type)",
"status": "killed",
"hidden": "7/9",
"failed_tests": [
"AUDIT_MATCHES",
"AUDIT_STRUCTURE"
]
},
{
"object": "{{P}}STOCK_LINE",
"mutant": "line 6: abap.dec(9,2) -> abap.dec(9,3) (decimals)",
"status": "killed",
"hidden": "8/9",
"failed_tests": [
"AMOUNT_TYPE"
]
},
{
"object": "{{P}}AUDIT_INFO",
"mutant": "line 5: abap.dats -> abap.tims (type)",
"status": "killed",
"hidden": "7/9",
"failed_tests": [
"AUDIT_MATCHES",
"AUDIT_STRUCTURE"
]
}
],
"valid": 4,
"killed": 4,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,6 @@
@EndUserText.label : 'Audit information'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}audit_info {
created_by : abap.char(12);
created_on : abap.dats;
}

View File

@@ -0,0 +1,10 @@
@EndUserText.label : 'Stock position line'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define structure {{p}}stock_line {
material : abap.char(18);
plant : abap.char(4);
net_amount : abap.dec(9,2);
currency : abap.char(5);
created_by : abap.char(12);
created_on : abap.dats;
}

View File

@@ -0,0 +1,68 @@
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_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS six_components FOR TESTING.
METHODS reads_stock_line FOR TESTING.
METHODS audit_matches FOR TESTING.
ENDCLASS.
CLASS {{p}}t16_test IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}STOCK' ) ) ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD setup.
go_env->clear_doubles( ).
DATA lt_stock TYPE STANDARD TABLE OF {{p}}stock WITH EMPTY KEY.
lt_stock = VALUE #(
( material = 'MAT-1' plant = 'P001' net_amount = '123.45'
currency = 'EUR' created_by = 'USER1' created_on = '20240115' ) ).
go_env->insert_test_data( lt_stock ).
ENDMETHOD.
METHOD six_components.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lt_fields) = lo_struct->get_ddic_field_list( ).
cl_abap_unit_assert=>assert_equals( exp = 6 act = lines( lt_fields ) ).
ENDMETHOD.
METHOD reads_stock_line.
DATA ls_line TYPE {{p}}stock_line.
SELECT SINGLE material, plant, net_amount, currency, created_by, created_on
FROM {{p}}stock
INTO @ls_line
WHERE material = 'MAT-1'.
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = ls_line-material ).
cl_abap_unit_assert=>assert_equals( exp = 'EUR' act = ls_line-currency ).
ENDMETHOD.
METHOD audit_matches.
DATA(lo_line) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}STOCK_LINE' ) ).
DATA(lo_audit) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( '{{P}}AUDIT_INFO' ) ).
DATA(lt_line) = lo_line->get_ddic_field_list( ).
DATA(lt_audit) = lo_audit->get_ddic_field_list( ).
LOOP AT lt_audit INTO DATA(ls_audit).
READ TABLE lt_line INTO DATA(ls_line) WITH KEY fieldname = ls_audit-fieldname.
cl_abap_unit_assert=>assert_subrc(
exp = 0 msg = |Component { ls_audit-fieldname } is missing| ).
cl_abap_unit_assert=>assert_equals( exp = ls_audit-leng act = ls_line-leng ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,14 @@
@EndUserText.label : 'Stock position'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}stock {
key client : abap.clnt not null;
key material : abap.char(18) not null;
key plant : abap.char(4) not null;
net_amount : abap.dec(9,2);
currency : abap.char(5);
created_by : abap.char(12);
created_on : abap.dats;
}

View File

@@ -0,0 +1,59 @@
# 1. Goal
A stock report and an OData service must use the same line type for warehouse
stock positions. Create the DDIC structure {{P}}STOCK_LINE and the DDIC
structure {{P}}AUDIT_INFO in package $TMP.
# 2. Open questions
None.
# 3. Context
- The table {{P}}STOCK exists in package $TMP. Its columns are, in this order:
MATERIAL (CHAR 18), PLANT (CHAR 4), NET_AMOUNT (DEC 9,2), CURRENCY (CHAR 5),
CREATED_BY (CHAR 12), CREATED_ON (DATS). The key is CLIENT, MATERIAL and
PLANT.
- The table {{P}}STOCK is empty. Test data comes from test doubles.
- {{P}}AUDIT_INFO is the audit part that other objects use, for example a
change log view and a service. {{P}}STOCK_LINE must carry the same audit
part.
- Both structures are used as line types in ABAP SQL and in an OData service.
# 4. Contract
- Create the DDIC structure {{P}}STOCK_LINE in package $TMP.
Components, in this order: material (CHAR 18), plant (CHAR 4),
net_amount (DEC 9,2), currency (CHAR 5), created_by (CHAR 12),
created_on (DATS).
- Create the DDIC structure {{P}}AUDIT_INFO in package $TMP.
Components, in this order: created_by (CHAR 12), created_on (DATS).
- {{P}}STOCK_LINE contains all six components itself. It does not use a DDIC
include.
# 5. Business rules
1. {{P}}STOCK_LINE has exactly six components.
2. The components of {{P}}STOCK_LINE are, in this order: material, plant,
net_amount, currency, created_by, created_on.
3. material is a character field of 18 characters.
4. plant is a character field of 4 characters.
5. net_amount is a packed number with 9 digits and 2 decimal places.
6. currency is a character field of 5 characters.
7. created_by is a character field of 12 characters.
8. created_on is a date field of type DATS.
9. {{P}}AUDIT_INFO has exactly two components, in this order: created_by
(CHAR 12) and created_on (DATS).
10. Every component of {{P}}STOCK_LINE is elementary. {{P}}STOCK_LINE is a
flat structure.
11. The components created_by and created_on of {{P}}STOCK_LINE have the same
data type and the same length as the components of {{P}}AUDIT_INFO.
12. A row of {{P}}STOCK can be read into a work area of type {{P}}STOCK_LINE
with ABAP SQL.
# 6. Constraints
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
- Package $TMP. Do not use transports.
- Coding standards: Clean ABAP. No ATC priority 1 or 2 findings.
- Out of scope: do not change the table {{P}}STOCK.
# 7. Acceptance
- Both structures are active and have no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests with CL_OSQL_TEST_ENVIRONMENT in a global test class
(for example {{P}}T16_TEST). The tests must prove rules 1 to 12.

View File

@@ -0,0 +1,74 @@
{
"id": "G1903",
"category": "B",
"object_type": "STRU",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 60,
"max_activations": 15
},
"seed": [
{
"type": "TABL",
"name": "{{P}}STOCK",
"file": "seed/stock.tabl.asabap",
"description": "Stock positions"
}
],
"contract": [
{
"type": "STRU",
"name": "{{P}}STOCK_LINE",
"fields": [
"material",
"plant",
"net_amount",
"currency",
"created_by",
"created_on"
]
},
{
"type": "STRU",
"name": "{{P}}AUDIT_INFO",
"fields": [
"created_by",
"created_on"
]
}
],
"out_of_scope": [
"{{P}}STOCK"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T16_HIDDEN",
"file": "hidden/t16_hidden.clas.abap",
"description": "T16 hidden tests"
}
],
"reference": [
{
"type": "STRU",
"name": "{{P}}AUDIT_INFO",
"file": "reference/audit_info.ddls.asddls",
"description": "Audit information structure"
},
{
"type": "STRU",
"name": "{{P}}STOCK_LINE",
"file": "reference/stock_line.ddls.asddls",
"description": "Stock line structure"
},
{
"type": "CLAS",
"name": "{{P}}T16_TEST",
"file": "reference/t16_test.clas.abap",
"description": "T16 own tests"
}
],
"craft_checks": []
}

View File

@@ -0,0 +1,45 @@
{
"id": "G1904",
"pool": "train",
"object_type": "CLAS",
"category": "D",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
}
],
"accepted": true,
"note": "mutation check rerun after exception-class mutants were added (3 of 3 killed)"
}

View File

@@ -0,0 +1,259 @@
CLASS {{p}}d02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS unknown_material_text FOR TESTING.
METHODS zero_quantity_text FOR TESTING.
METHODS over_limit_text FOR TESTING.
METHODS default_text_cases FOR TESTING.
METHODS context_of_each_case FOR TESTING.
METHODS unknown_case_keeps_data FOR TESTING.
METHODS text_via_cx_root FOR TESTING.
METHODS case_constant_values FOR TESTING.
METHODS is_static_check_exception FOR TESTING.
METHODS instances_keep_own_data FOR TESTING.
METHODS same_data_different_case FOR TESTING.
METHODS text_exact_length FOR TESTING.
METHODS caught_error
IMPORTING iv_case TYPE {{p}}if_issue_error_info=>ty_case
iv_material TYPE string
iv_quantity TYPE i
iv_limit TYPE i
RETURNING VALUE(ro_error) TYPE REF TO {{p}}cx_issue_error.
ENDCLASS.
CLASS {{p}}d02_hidden IMPLEMENTATION.
METHOD caught_error.
TRY.
RAISE EXCEPTION TYPE {{p}}cx_issue_error
EXPORTING
iv_case = iv_case
iv_material = iv_material
iv_quantity = iv_quantity
iv_limit = iv_limit.
CATCH {{p}}cx_issue_error INTO ro_error.
ENDTRY.
ENDMETHOD.
METHOD unknown_material_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'MAT-1'
iv_quantity = 5
iv_limit = 100 ).
cl_abap_unit_assert=>assert_equals( exp = `Material MAT-1 is not known`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD zero_quantity_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = 'MAT-2'
iv_quantity = 0
iv_limit = 50 ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity must not be zero for material MAT-2`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD over_limit_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'MAT-3'
iv_quantity = 120
iv_limit = 100 ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity 120 exceeds limit 100 for material MAT-3`
act = lo_error->get_text( ) ).
DATA(lo_big) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'M-BIG'
iv_quantity = 12345
iv_limit = 67890 ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity 12345 exceeds limit 67890 for material M-BIG`
act = lo_big->get_text( ) ).
ENDMETHOD.
METHOD default_text_cases.
DATA(lo_other) = caught_error( iv_case = 'SOMETHING_ELSE'
iv_material = 'MAT-4'
iv_quantity = 7
iv_limit = 70 ).
cl_abap_unit_assert=>assert_equals( exp = `Stock issue failed for material MAT-4`
act = lo_other->get_text( ) ).
DATA(lo_empty) = caught_error( iv_case = ''
iv_material = 'MAT-5'
iv_quantity = 1
iv_limit = 10 ).
cl_abap_unit_assert=>assert_equals( exp = `Stock issue failed for material MAT-5`
act = lo_empty->get_text( ) ).
ENDMETHOD.
METHOD context_of_each_case.
DATA(lo_unknown) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'M-1'
iv_quantity = 11
iv_limit = 111 ).
DATA(lo_zero) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = 'M-2'
iv_quantity = 0
iv_limit = 222 ).
DATA(lo_over) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'M-3'
iv_quantity = 33
iv_limit = 333 ).
DATA lo_info TYPE REF TO {{p}}if_issue_error_info.
lo_info = lo_unknown.
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_issue_error=>c_case-unknown_material
act = lo_info->get_case( ) ).
cl_abap_unit_assert=>assert_equals( exp = `M-1` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 11 act = lo_info->get_quantity( ) ).
cl_abap_unit_assert=>assert_equals( exp = 111 act = lo_info->get_limit( ) ).
lo_info = lo_zero.
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_issue_error=>c_case-zero_quantity
act = lo_info->get_case( ) ).
cl_abap_unit_assert=>assert_equals( exp = `M-2` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lo_info->get_quantity( ) ).
cl_abap_unit_assert=>assert_equals( exp = 222 act = lo_info->get_limit( ) ).
lo_info = lo_over.
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_issue_error=>c_case-over_limit
act = lo_info->get_case( ) ).
cl_abap_unit_assert=>assert_equals( exp = `M-3` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 33 act = lo_info->get_quantity( ) ).
cl_abap_unit_assert=>assert_equals( exp = 333 act = lo_info->get_limit( ) ).
ENDMETHOD.
METHOD unknown_case_keeps_data.
DATA(lo_error) = caught_error( iv_case = 'SOMETHING_ELSE'
iv_material = 'M-X'
iv_quantity = 55
iv_limit = 66 ).
DATA lo_info TYPE REF TO {{p}}if_issue_error_info.
lo_info = lo_error.
cl_abap_unit_assert=>assert_equals( exp = 'SOMETHING_ELSE' act = lo_info->get_case( ) ).
cl_abap_unit_assert=>assert_equals( exp = `M-X` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 55 act = lo_info->get_quantity( ) ).
cl_abap_unit_assert=>assert_equals( exp = 66 act = lo_info->get_limit( ) ).
cl_abap_unit_assert=>assert_equals( exp = `Stock issue failed for material M-X`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD text_via_cx_root.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = 'MAT-6'
iv_quantity = 0
iv_limit = 60 ).
DATA lo_root TYPE REF TO cx_root.
lo_root = lo_error.
cl_abap_unit_assert=>assert_equals( exp = `Quantity must not be zero for material MAT-6`
act = lo_root->get_text( ) ).
ENDMETHOD.
METHOD case_constant_values.
cl_abap_unit_assert=>assert_equals( exp = 'UNKNOWN_MATERIAL'
act = {{p}}cx_issue_error=>c_case-unknown_material ).
cl_abap_unit_assert=>assert_equals( exp = 'ZERO_QUANTITY'
act = {{p}}cx_issue_error=>c_case-zero_quantity ).
cl_abap_unit_assert=>assert_equals( exp = 'OVER_LIMIT'
act = {{p}}cx_issue_error=>c_case-over_limit ).
ENDMETHOD.
METHOD is_static_check_exception.
DATA lo_caught TYPE REF TO cx_static_check.
TRY.
RAISE EXCEPTION TYPE {{p}}cx_issue_error
EXPORTING
iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'MAT-7'
iv_quantity = 3
iv_limit = 30.
CATCH cx_static_check INTO lo_caught.
ENDTRY.
cl_abap_unit_assert=>assert_bound( act = lo_caught ).
ENDMETHOD.
METHOD instances_keep_own_data.
DATA(lo_first) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'FIRST'
iv_quantity = 1
iv_limit = 10 ).
DATA(lo_second) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'SECOND'
iv_quantity = 2
iv_limit = 20 ).
DATA lo_info TYPE REF TO {{p}}if_issue_error_info.
lo_info = lo_first.
cl_abap_unit_assert=>assert_equals( exp = `FIRST` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = `Material FIRST is not known`
act = lo_first->get_text( ) ).
lo_info = lo_second.
cl_abap_unit_assert=>assert_equals( exp = `SECOND` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity 2 exceeds limit 20 for material SECOND`
act = lo_second->get_text( ) ).
lo_info = lo_first.
cl_abap_unit_assert=>assert_equals( exp = `FIRST` act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lo_info->get_quantity( ) ).
ENDMETHOD.
METHOD same_data_different_case.
DATA lv_material TYPE string.
DATA lv_quantity TYPE i.
DATA lv_limit TYPE i.
lv_material = 'MAT-S'.
lv_quantity = 5.
lv_limit = 10.
DATA(lo_unknown) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = lv_material
iv_quantity = lv_quantity
iv_limit = lv_limit ).
DATA(lo_zero) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = lv_material
iv_quantity = lv_quantity
iv_limit = lv_limit ).
DATA(lo_over) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = lv_material
iv_quantity = lv_quantity
iv_limit = lv_limit ).
cl_abap_unit_assert=>assert_equals( exp = `Material MAT-S is not known`
act = lo_unknown->get_text( ) ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity must not be zero for material MAT-S`
act = lo_zero->get_text( ) ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity 5 exceeds limit 10 for material MAT-S`
act = lo_over->get_text( ) ).
ENDMETHOD.
METHOD text_exact_length.
DATA(lo_unknown) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'MAT-1'
iv_quantity = 5
iv_limit = 100 ).
DATA(lo_zero) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = 'MAT-2'
iv_quantity = 0
iv_limit = 50 ).
DATA(lo_over) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'MAT-3'
iv_quantity = 120
iv_limit = 100 ).
DATA lv_unknown TYPE string.
DATA lv_zero TYPE string.
DATA lv_over TYPE string.
lv_unknown = `Material MAT-1 is not known`.
lv_zero = `Quantity must not be zero for material MAT-2`.
lv_over = `Quantity 120 exceeds limit 100 for material MAT-3`.
cl_abap_unit_assert=>assert_equals( exp = strlen( lv_unknown )
act = strlen( lo_unknown->get_text( ) ) ).
cl_abap_unit_assert=>assert_equals( exp = strlen( lv_zero )
act = strlen( lo_zero->get_text( ) ) ).
cl_abap_unit_assert=>assert_equals( exp = strlen( lv_over )
act = strlen( lo_over->get_text( ) ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,36 @@
{
"task": "G1904",
"mutants": [
{
"object": "{{P}}CX_ISSUE_ERROR",
"mutant": "line 12: VALUE 'UNKNOWN_MATERIAL' -> VALUE 'ZNKNOWN_MATERIAL' (lit)",
"status": "killed",
"hidden": "11/12",
"failed_tests": [
"CASE_CONSTANT_VALUES"
]
},
{
"object": "{{P}}CX_ISSUE_ERROR",
"mutant": "line 13: VALUE 'ZERO_QUANTITY' -> VALUE 'YERO_QUANTITY' (lit)",
"status": "killed",
"hidden": "11/12",
"failed_tests": [
"CASE_CONSTANT_VALUES"
]
},
{
"object": "{{P}}CX_ISSUE_ERROR",
"mutant": "line 14: VALUE 'OVER_LIMIT' -> VALUE 'ZVER_LIMIT' (lit)",
"status": "killed",
"hidden": "11/12",
"failed_tests": [
"CASE_CONSTANT_VALUES"
]
}
],
"valid": 3,
"killed": 3,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,73 @@
CLASS {{p}}cx_issue_error DEFINITION
PUBLIC
INHERITING FROM cx_static_check
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_issue_error_info.
CONSTANTS:
BEGIN OF c_case,
unknown_material TYPE {{p}}if_issue_error_info=>ty_case VALUE 'UNKNOWN_MATERIAL',
zero_quantity TYPE {{p}}if_issue_error_info=>ty_case VALUE 'ZERO_QUANTITY',
over_limit TYPE {{p}}if_issue_error_info=>ty_case VALUE 'OVER_LIMIT',
END OF c_case.
METHODS constructor
IMPORTING
iv_case TYPE {{p}}if_issue_error_info=>ty_case
iv_material TYPE string
iv_quantity TYPE i
iv_limit TYPE i.
METHODS get_text REDEFINITION.
PRIVATE SECTION.
DATA mv_case TYPE {{p}}if_issue_error_info=>ty_case.
DATA mv_material TYPE string.
DATA mv_quantity TYPE i.
DATA mv_limit TYPE i.
ENDCLASS.
CLASS {{p}}cx_issue_error IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_case = iv_case.
mv_material = iv_material.
mv_quantity = iv_quantity.
mv_limit = iv_limit.
ENDMETHOD.
METHOD {{p}}if_issue_error_info~get_case.
rv_case = mv_case.
ENDMETHOD.
METHOD {{p}}if_issue_error_info~get_material.
rv_material = mv_material.
ENDMETHOD.
METHOD {{p}}if_issue_error_info~get_quantity.
rv_quantity = mv_quantity.
ENDMETHOD.
METHOD {{p}}if_issue_error_info~get_limit.
rv_limit = mv_limit.
ENDMETHOD.
METHOD get_text.
CASE mv_case.
WHEN c_case-unknown_material.
result = |Material { mv_material } is not known|.
WHEN c_case-zero_quantity.
result = |Quantity must not be zero for material { mv_material }|.
WHEN c_case-over_limit.
result = |Quantity { mv_quantity } exceeds limit { mv_limit } for material { mv_material }|.
WHEN OTHERS.
result = |Stock issue failed for material { mv_material }|.
ENDCASE.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,81 @@
CLASS ltc_issue_error DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS unknown_material_text FOR TESTING.
METHODS zero_quantity_text FOR TESTING.
METHODS over_limit_text FOR TESTING.
METHODS default_text FOR TESTING.
METHODS keeps_context_data FOR TESTING.
METHODS caught_error
IMPORTING iv_case TYPE {{p}}if_issue_error_info=>ty_case
iv_material TYPE string
iv_quantity TYPE i
iv_limit TYPE i
RETURNING VALUE(ro_error) TYPE REF TO {{p}}cx_issue_error.
ENDCLASS.
CLASS ltc_issue_error IMPLEMENTATION.
METHOD caught_error.
TRY.
RAISE EXCEPTION TYPE {{p}}cx_issue_error
EXPORTING
iv_case = iv_case
iv_material = iv_material
iv_quantity = iv_quantity
iv_limit = iv_limit.
CATCH {{p}}cx_issue_error INTO ro_error.
ENDTRY.
ENDMETHOD.
METHOD unknown_material_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-unknown_material
iv_material = 'MAT-A'
iv_quantity = 1
iv_limit = 10 ).
cl_abap_unit_assert=>assert_equals( exp = `Material MAT-A is not known`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD zero_quantity_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-zero_quantity
iv_material = 'MAT-B'
iv_quantity = 0
iv_limit = 20 ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity must not be zero for material MAT-B`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD over_limit_text.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'MAT-C'
iv_quantity = 30
iv_limit = 20 ).
cl_abap_unit_assert=>assert_equals( exp = `Quantity 30 exceeds limit 20 for material MAT-C`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD default_text.
DATA(lo_error) = caught_error( iv_case = 'OTHER'
iv_material = 'MAT-D'
iv_quantity = 5
iv_limit = 50 ).
cl_abap_unit_assert=>assert_equals( exp = `Stock issue failed for material MAT-D`
act = lo_error->get_text( ) ).
ENDMETHOD.
METHOD keeps_context_data.
DATA(lo_error) = caught_error( iv_case = {{p}}cx_issue_error=>c_case-over_limit
iv_material = 'MAT-E'
iv_quantity = 99
iv_limit = 90 ).
DATA lo_info TYPE REF TO {{p}}if_issue_error_info.
lo_info = lo_error.
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_issue_error=>c_case-over_limit
act = lo_info->get_case( ) ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-E' act = lo_info->get_material( ) ).
cl_abap_unit_assert=>assert_equals( exp = 99 act = lo_info->get_quantity( ) ).
cl_abap_unit_assert=>assert_equals( exp = 90 act = lo_info->get_limit( ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,12 @@
INTERFACE {{p}}if_issue_error_info PUBLIC.
TYPES ty_case TYPE c LENGTH 20.
METHODS get_case
RETURNING VALUE(rv_case) TYPE ty_case.
METHODS get_material
RETURNING VALUE(rv_material) TYPE string.
METHODS get_quantity
RETURNING VALUE(rv_quantity) TYPE i.
METHODS get_limit
RETURNING VALUE(rv_limit) TYPE i.
ENDINTERFACE.

View File

@@ -0,0 +1,58 @@
# 1. Goal
A stock issue program must stop with a clear reason when it cannot issue a
quantity of a material. The caller needs one exception class that carries the
reason and the data of the failed issue.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_ISSUE_ERROR_INFO exists in package $TMP. It is active.
- The interface has the type TY_CASE (CHAR 20) and the methods GET_CASE,
GET_MATERIAL, GET_QUANTITY and GET_LIMIT.
- The exception class {{P}}CX_ISSUE_ERROR is raised by the stock issue program
and caught by the caller.
# 4. Contract
- Create the class {{P}}CX_ISSUE_ERROR in package $TMP.
- The class inherits from CX_STATIC_CHECK.
- The class is public, final, and has a public constructor.
- The class implements {{P}}IF_ISSUE_ERROR_INFO.
- The class has the constant structure C_CASE. Its components are
UNKNOWN_MATERIAL, ZERO_QUANTITY and OVER_LIMIT. The type of each component is
{{P}}IF_ISSUE_ERROR_INFO=>TY_CASE. The value of each component is its name.
- The constructor has these importing parameters, in this order:
IV_CASE (type {{P}}IF_ISSUE_ERROR_INFO=>TY_CASE), IV_MATERIAL (type STRING),
IV_QUANTITY (type I), IV_LIMIT (type I).
- The class redefines the inherited method GET_TEXT. GET_TEXT has no importing
parameter and returns the text as a string.
- Do not add other public methods.
# 5. Business rules
1. The constructor stores IV_CASE, IV_MATERIAL, IV_QUANTITY and IV_LIMIT.
2. {{P}}IF_ISSUE_ERROR_INFO~GET_CASE returns the stored case, ~GET_MATERIAL the
stored material, ~GET_QUANTITY the stored quantity and ~GET_LIMIT the stored
limit.
3. GET_TEXT returns one text that depends on the stored case:
- C_CASE-UNKNOWN_MATERIAL: 'Material <material> is not known'
- C_CASE-ZERO_QUANTITY: 'Quantity must not be zero for material <material>'
- C_CASE-OVER_LIMIT: 'Quantity <quantity> exceeds limit <limit> for material
<material>'
- any other case, including an empty case: 'Stock issue failed for material
<material>'
<material>, <quantity> and <limit> are the stored values. Write the quantity
and the limit as plain digits, for example 120 and 100. The text has no
leading and no trailing blanks.
4. The class is a static check exception. A caller must handle it or declare it
in the RAISING clause.
# 6. Constraints
- Release target: SAP_BASIS 816.
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. No comment that restates the code.
- Out of scope: do not change {{P}}IF_ISSUE_ERROR_INFO.
# 7. Acceptance
- The class is active and has no syntax error.
- The hidden tests pass.
- Write your own ABAP Unit tests for the class.

View File

@@ -0,0 +1,50 @@
{
"id": "G1904",
"category": "D",
"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_ISSUE_ERROR_INFO",
"file": "seed/if_issue_error_info.intf.abap",
"description": "Contract interface with the case type and the getters of the issue error"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}CX_ISSUE_ERROR",
"implements": "{{P}}IF_ISSUE_ERROR_INFO"
}
],
"out_of_scope": [
"{{P}}IF_ISSUE_ERROR_INFO"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}D02_HIDDEN",
"file": "hidden/d02_hidden.clas.abap",
"description": "D02 hidden tests for the issue error exception class"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}CX_ISSUE_ERROR",
"file": "reference/cx_issue_error.clas.abap",
"description": "Exception class with case constants, context data and a redefined GET_TEXT",
"testclasses_file": "reference/cx_issue_error.testclasses.abap"
}
],
"craft_checks": [
"method_length"
]
}

View File

@@ -0,0 +1,36 @@
{
"id": "G1140",
"pool": "train",
"object_type": "PROG",
"category": "B",
"attempts": [
{
"stage": "bundle",
"errors": [
"Too close to task G0020 (similarity spec 0.67, rules 0.49, names 1.00). Choose a different business topic and different object names.",
"Too close to task G0183 (similarity spec 0.60, rules 0.00, names 1.00). Choose a different business topic and different object names."
],
"by_check": {
"extra": 2
}
},
{
"stage": "validate",
"oracle": 86.7,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 2,
"killed": 2,
"ok": true
}
}
],
"accepted": true,
"error_kind": null,
"spent_total": 94.615
}

View File

@@ -0,0 +1,31 @@
{
"id": "G1900",
"pool": "train",
"object_type": "INTF",
"category": "A",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 80.6,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true,
"kind": "INTF",
"difficulty": 2
}

View File

@@ -0,0 +1,21 @@
{
"id": "G1901",
"pool": "train",
"object_type": "TABL",
"category": "B",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 4,
"killed": 4,
"ok": true
}
}
],
"accepted": true,
"kind": "TABL",
"difficulty": 2
}

View File

@@ -0,0 +1,26 @@
{
"id": "G1902",
"pool": "train",
"object_type": "MSAG",
"category": "D",
"attempts": [
{
"stage": "validate",
"oracle": 85.0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true,
"kind": "MSAG",
"difficulty": 2
}

View File

@@ -0,0 +1,31 @@
{
"id": "G1903",
"pool": "train",
"object_type": "STRU",
"category": "B",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 4,
"killed": 4,
"ok": true
}
}
],
"accepted": true,
"kind": "STRU",
"difficulty": 2
}

View File

@@ -0,0 +1,47 @@
{
"id": "G1904",
"pool": "train",
"object_type": "CLAS",
"category": "D",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 0,
"killed": 0,
"ok": false
}
}
],
"accepted": true,
"note": "mutation check rerun after exception-class mutants were added (3 of 3 killed)",
"kind": "EXC",
"difficulty": 2
}

3756
tasks_gen/train/plan2.json Normal file

File diff suppressed because it is too large Load Diff