|
33 | 33 | -export([number_shrinker/4, union_first_choice_shrinker/3,
|
34 | 34 | union_recursive_shrinker/3]).
|
35 | 35 | -export([split_shrinker/3, remove_shrinker/3]).
|
| 36 | +-export([map_remove_shrinker/3, map_key_shrinker/3, map_value_shrinker/3]). |
36 | 37 |
|
37 | 38 | -export_type([state/0, shrinker/0]).
|
38 | 39 |
|
@@ -397,6 +398,112 @@ elements_shrinker(Instance, Type,
|
397 | 398 | elements_shrinker(Instance, Type,
|
398 | 399 | {inner,Indices,GetElemType,{shrunk,N,InnerState}}).
|
399 | 400 |
|
| 401 | +-spec map_remove_shrinker( |
| 402 | + proper_gen:imm_instance(), proper_types:type(), state() |
| 403 | +) -> {[proper_gen:imm_instance()], state()}. |
| 404 | +map_remove_shrinker(Instance, Type, init) when is_map(Instance) -> |
| 405 | + GetKeys = proper_types:get_prop(get_keys, Type), |
| 406 | + Keys = GetKeys(Instance), |
| 407 | + map_remove_shrinker(Instance, Type, {shrunk, 1, {keys, ordsets:new(), Keys}}); |
| 408 | +map_remove_shrinker(Instance, _Type, {keys, _Checked, []}) when is_map(Instance) -> |
| 409 | + {[], done}; |
| 410 | +map_remove_shrinker(Instance, Type, {keys, Checked, [Key | Rest]}) when is_map(Instance) -> |
| 411 | + Remove = proper_types:get_prop(remove, Type), |
| 412 | + {[Remove(Key, Instance)], {keys, ordsets:add_element(Key, Checked), Rest}}; |
| 413 | +map_remove_shrinker(Instance, Type, {shrunk, 1, {keys, Checked, ToCheck}}) when is_map(Instance) -> |
| 414 | + %% GetKeys = proper_types:get_prop(get_keys, Type), |
| 415 | + %% Keys = ordsets:from_list(GetKeys(Instance)), |
| 416 | + %% NewToCheck = ordsets:subtract(Keys, Checked), |
| 417 | + map_remove_shrinker(Instance, Type, {keys, Checked, ToCheck}). |
| 418 | + |
| 419 | +-spec map_value_shrinker( |
| 420 | + proper_gen:imm_instance(), proper_types:type(), state() |
| 421 | +) -> {[proper_gen:imm_instance()], state()}. |
| 422 | +map_value_shrinker(Instance, _Type, init) when map_size(Instance) =:= 0 -> |
| 423 | + {[], done}; |
| 424 | +map_value_shrinker(Instance, Type, init) when is_map(Instance) -> |
| 425 | + GetKeys = proper_types:get_prop(get_keys, Type), |
| 426 | + TypeMap = proper_types:get_prop(internal_types, Type), |
| 427 | + Keys = GetKeys(Instance), |
| 428 | + ValueTypeMap = maps:map(fun(Key, Value) -> |
| 429 | + {_KeyType, ValueType} = get_map_field_candidates(Key, Value, TypeMap), |
| 430 | + ValueType |
| 431 | + end, Instance), |
| 432 | + map_value_shrinker(Instance, Type, {inner, Keys, ValueTypeMap, init}); |
| 433 | +map_value_shrinker(Instance, _Type, {inner, [], _ValueTypeMap, init}) when is_map(Instance) -> |
| 434 | + {[], done}; |
| 435 | +map_value_shrinker( |
| 436 | + Instance, Type, {inner, [_Key | Rest], ValueTypeMap, done} |
| 437 | +) when is_map(Instance) -> |
| 438 | + map_value_shrinker(Instance, Type, {inner, Rest, ValueTypeMap, init}); |
| 439 | +map_value_shrinker( |
| 440 | + Instance, Type, {inner, Keys = [Key | _], ValueTypeMap, InnerState} |
| 441 | +) when is_map(Instance) -> |
| 442 | + Retrieve = proper_types:get_prop(retrieve, Type), |
| 443 | + Update = proper_types:get_prop(update, Type), |
| 444 | + Value = Retrieve(Key, Instance), |
| 445 | + ValueType = Retrieve(Key, ValueTypeMap), |
| 446 | + {NewValues, NewInnerState} = shrink(Value, ValueType, InnerState), |
| 447 | + NewInstances = [Update(Key, NewValue, Instance) || NewValue <- NewValues], |
| 448 | + {NewInstances, {inner, Keys, ValueTypeMap, NewInnerState}}; |
| 449 | +map_value_shrinker( |
| 450 | + Instance, Type, {shrunk, N, {inner, ToCheck, ValueTypeMap, InnerState}} |
| 451 | +) when is_map(Instance) -> |
| 452 | + map_value_shrinker( |
| 453 | + Instance, Type, {inner, ToCheck, ValueTypeMap, {shrunk, N, InnerState}} |
| 454 | + ). |
| 455 | + |
| 456 | +-spec map_key_shrinker( |
| 457 | + proper_gen:imm_instance(), proper_types:type(), state() |
| 458 | +) -> {[proper_gen:imm_instance()], state()}. |
| 459 | +map_key_shrinker(Instance, Type, init) when is_map(Instance) -> |
| 460 | + GetKeys = proper_types:get_prop(get_keys, Type), |
| 461 | + TypeMap = proper_types:get_prop(internal_types, Type), |
| 462 | + Keys = GetKeys(Instance), |
| 463 | + KeyTypeMap = maps:map(fun(Key, Value) -> |
| 464 | + {KeyType, _ValueType} = get_map_field_candidates(Key, Value, TypeMap), |
| 465 | + KeyType |
| 466 | + end, Instance), |
| 467 | + map_key_shrinker(Instance, Type, {inner, Keys, KeyTypeMap, init}); |
| 468 | +map_key_shrinker(Instance, _Type, {inner, [], _ValueTypeMap, init}) when is_map(Instance) -> |
| 469 | + {[], done}; |
| 470 | +map_key_shrinker( |
| 471 | + Instance, Type, {inner, [_Key | Rest], KeyTypeMap, done} |
| 472 | +) when is_map(Instance) -> |
| 473 | + map_key_shrinker(Instance, Type, {inner, Rest, KeyTypeMap, init}); |
| 474 | +map_key_shrinker( |
| 475 | + Instance, Type, {inner, Keys = [Key | _], KeyTypeMap, InnerState} |
| 476 | +) when is_map(Instance) -> |
| 477 | + Retrieve = proper_types:get_prop(retrieve, Type), |
| 478 | + Update = proper_types:get_prop(update, Type), |
| 479 | + Remove = proper_types:get_prop(remove, Type), |
| 480 | + Value = Retrieve(Key, Instance), |
| 481 | + KeyType = Retrieve(Key, KeyTypeMap), |
| 482 | + {NewKeys, NewInnerState} = shrink(Key, KeyType, InnerState), |
| 483 | + InstanceWithoutKey = Remove(Key, Instance), |
| 484 | + NewInstances = [ |
| 485 | + Update(NewKey, Value, InstanceWithoutKey) || NewKey <- NewKeys |
| 486 | + ], |
| 487 | + {NewInstances, {inner, Keys, KeyTypeMap, NewInnerState}}; |
| 488 | +map_key_shrinker( |
| 489 | + Instance, Type, {shrunk, N, {inner, ToCheck, KeyTypeMap, InnerState}} |
| 490 | +) when is_map(Instance) -> |
| 491 | + map_key_shrinker( |
| 492 | + Instance, Type, {inner, ToCheck, KeyTypeMap, {shrunk, N, InnerState}} |
| 493 | + ). |
| 494 | + |
| 495 | +get_map_field_candidates(Key, Value, TypeMap) -> |
| 496 | + Candidates = maps:filter(fun(KeyType, ValueType) -> |
| 497 | + (Key =:= KeyType orelse |
| 498 | + proper_types:is_type(Key) andalso |
| 499 | + proper_types:is_instance(Key, KeyType)) |
| 500 | + andalso |
| 501 | + (Key =:= KeyType orelse |
| 502 | + proper_types:is_type(Value) andalso |
| 503 | + proper_types:is_instance(Value, ValueType)) |
| 504 | + end, TypeMap), |
| 505 | + {KeyType, ValueType, _} = maps:next(maps:iterator(Candidates)), |
| 506 | + {KeyType, ValueType}. |
400 | 507 |
|
401 | 508 | %%------------------------------------------------------------------------------
|
402 | 509 | %% Custom shrinkers
|
|
0 commit comments