From ff3e46ca6edf7a5f1459f11c3baea5ccd5dabb83 Mon Sep 17 00:00:00 2001 From: mizchi Date: Tue, 25 Aug 2026 03:43:40 +0900 Subject: [PATCH 1/2] Improve QuickCheck shrink candidate ordering --- quickcheck/README.mbt.md | 2 +- quickcheck/driver_test.mbt | 34 ++++++ quickcheck/shrink/collection.mbt | 4 +- quickcheck/shrink/collection_test.mbt | 106 +++++++++++-------- quickcheck/shrink/composite.mbt | 2 +- quickcheck/shrink/composite_test.mbt | 76 +++++++------- quickcheck/shrink/shrink.mbt | 32 ++++-- quickcheck/shrink/shrink_test.mbt | 143 +++++++++++++------------- quickcheck/shrink/utils.mbt | 2 +- 9 files changed, 233 insertions(+), 168 deletions(-) diff --git a/quickcheck/README.mbt.md b/quickcheck/README.mbt.md index 4130154109..0fc53473a9 100644 --- a/quickcheck/README.mbt.md +++ b/quickcheck/README.mbt.md @@ -289,7 +289,7 @@ test "custom type shrinking" { debug_inspect( @quickcheck.Shrink::shrink(point).collect(), content=( - #|[{ x: 1, y: 1 }, { x: 0, y: 1 }, { x: 2, y: 0 }] + #|[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 0 }] ), ) } diff --git a/quickcheck/driver_test.mbt b/quickcheck/driver_test.mbt index bae41638eb..49acfa67ef 100644 --- a/quickcheck/driver_test.mbt +++ b/quickcheck/driver_test.mbt @@ -534,3 +534,37 @@ test "context is attached to raised failures too" { ), ) } + +///| +priv struct LargeShrinkInput(Int) derive(Debug) + +///| +impl @quickcheck.Arbitrary for LargeShrinkInput with fn arbitrary(_, _) { + LargeShrinkInput(10000) +} + +///| +impl @shrink.Shrink for LargeShrinkInput with fn shrink(input) { + @shrink.Shrink::shrink(input.0).map(value => LargeShrinkInput(value)) +} + +///| +test "numeric shrinking reaches the boundary within the default budget" { + let report = @quickcheck.report( + (input : LargeShrinkInput) => input.0 < 5000, + count=1, + max_size=0, + ) + debug_inspect( + report, + content=( + #|Falsified( + #| counterexample=LargeShrinkInput(5000), + #| tests=1, + #| size=0, + #| shrinks=1, + #| shrink_attempts=15, + #|) + ), + ) +} diff --git a/quickcheck/shrink/collection.mbt b/quickcheck/shrink/collection.mbt index 9196cc15cc..660981b072 100644 --- a/quickcheck/shrink/collection.mbt +++ b/quickcheck/shrink/collection.mbt @@ -26,9 +26,9 @@ pub impl[T : Shrink] Shrink for @list.List[T] with fn shrink(xs) { } [ - for k = n / 2; k > 0; k = k / 2 => k + for k = n; k > 0; k = k / 2 => k ] - .rev_iter() + .iter() .flat_map(k => removes_list(k, n, xs)) .concat(shr_sub_terms(xs)) } diff --git a/quickcheck/shrink/collection_test.mbt b/quickcheck/shrink/collection_test.mbt index 1cc08ccf2d..b8651180b1 100644 --- a/quickcheck/shrink/collection_test.mbt +++ b/quickcheck/shrink/collection_test.mbt @@ -42,33 +42,43 @@ test "shrink int list" { @shrink.Shrink::shrink(il).collect(), content=( #|[ + #| , + #| , + #| , #| , #| , #| , #| , #| , #| , - #| , - #| , #| , - #| , #| , - #| , + #| , #| , - #| , - #| , + #| , #| , - #| , - #| , + #| , + #| , #| , - #| , - #| , + #| , + #| , #| , + #| , + #| , #|] ), ) } +///| +test "single-element list can shrink to empty" { + let input : @list.List[Int] = List([1]) + debug_inspect( + @shrink.Shrink::shrink(input).collect(), + content="[, ]", + ) +} + ///| test "shrink queue" { let input = @queue.Queue([true, false, true]) @@ -97,11 +107,11 @@ test "shrink immutable vector" { #| , #| , #| , - #| , #| , - #| , - #| , + #| , #| , + #| , + #| , #|] ), ) @@ -118,13 +128,13 @@ test "shrink mutable hash set" { #| , #| , #| , - #| , - #| , - #| , #| , - #| , - #| , + #| , + #| , + #| , #| , + #| , + #| , #|] ), ) @@ -140,11 +150,11 @@ test "shrink mutable sorted set" { #| , #| , #| , - #| , #| , - #| , - #| , + #| , #| , + #| , + #| , #|] ), ) @@ -161,11 +171,11 @@ test "shrink immutable hash set" { #| , #| , #| , - #| , #| , - #| , - #| , + #| , #| , + #| , + #| , #|] ), ) @@ -181,11 +191,11 @@ test "shrink immutable sorted set" { #| , #| , #| , - #| , #| , - #| , - #| , + #| , #| , + #| , + #| , #|] ), ) @@ -205,8 +215,8 @@ test "shrink builtin map" { #| [], #| [(1, false)], #| [(2, true)], - #| [(1, false)], #| [(0, true), (1, false)], + #| [(1, false)], #| [(2, false), (1, false)], #| [(2, true), (0, false)], #|] @@ -220,7 +230,12 @@ test "shrink linked set" { let candidates = @shrink.Shrink::shrink(input) .map(@set.Set::to_array) .collect() - debug_inspect(candidates, content="[[], [1], [2], [1], [0, 1], [2, 0]]") + debug_inspect( + candidates, + content=( + #|[[], [1], [2], [0, 1], [1], [2, 0]] + ), + ) } ///| @@ -229,7 +244,12 @@ test "shrink deque" { let candidates = @shrink.Shrink::shrink(input) .map(@deque.Deque::to_array) .collect() - debug_inspect(candidates, content="[[], [1], [2], [1, 1], [0, 1], [2, 0]]") + debug_inspect( + candidates, + content=( + #|[[], [1], [2], [0, 1], [1, 1], [2, 0]] + ), + ) } ///| @@ -240,7 +260,9 @@ test "shrink mutable priority queue" { .collect() debug_inspect( candidates, - content="[[], [2], [4], [3, 2], [2, 2], [2, 0], [4, 1], [4, 0]]", + content=( + #|[[], [2], [4], [2, 0], [2, 2], [3, 2], [4, 0], [4, 1]] + ), ) } @@ -252,7 +274,9 @@ test "shrink immutable priority queue" { .collect() debug_inspect( candidates, - content="[[], [2], [4], [3, 2], [2, 2], [2, 0], [4, 1], [4, 0]]", + content=( + #|[[], [2], [4], [2, 0], [2, 2], [3, 2], [4, 0], [4, 1]] + ), ) } @@ -268,8 +292,8 @@ test "shrink mutable hash map" { #| , #| , #| , - #| , #| , + #| , #| , #|] ), @@ -286,8 +310,8 @@ test "shrink mutable sorted map" { #| , #| , #| , - #| , #| , + #| , #| , #| , #|] @@ -306,16 +330,16 @@ test "shrink immutable hash map" { #| , #| , #| , - #| , #| , - #| , - #| , + #| , #| , - #| , - #| , + #| , + #| , #| , - #| , + #| , + #| , #| , + #| , #|] ), ) @@ -332,8 +356,8 @@ test "shrink immutable sorted map" { #| , #| , #| , - #| , #| , + #| , #| , #|] ), diff --git a/quickcheck/shrink/composite.mbt b/quickcheck/shrink/composite.mbt index 7e0de29849..fbb052440d 100644 --- a/quickcheck/shrink/composite.mbt +++ b/quickcheck/shrink/composite.mbt @@ -16,7 +16,7 @@ pub impl[T : Shrink] Shrink for T? with fn shrink(x) { match x { None => [||] - Some(v) => Shrink::shrink(v).map(v1 => Some(v1)).concat([|None|]) + Some(v) => [|None|].concat(Shrink::shrink(v).map(v1 => Some(v1))) } } diff --git a/quickcheck/shrink/composite_test.mbt b/quickcheck/shrink/composite_test.mbt index 5e85a16e8e..cb9aa35fe2 100644 --- a/quickcheck/shrink/composite_test.mbt +++ b/quickcheck/shrink/composite_test.mbt @@ -24,17 +24,17 @@ test "shrink option" { @shrink.Shrink::shrink(Some(1000)).collect(), content=( #|[ - #| Some(999), - #| Some(997), - #| Some(993), - #| Some(985), - #| Some(969), - #| Some(938), - #| Some(875), - #| Some(750), - #| Some(500), - #| Some(0), #| None, + #| Some(0), + #| Some(500), + #| Some(750), + #| Some(875), + #| Some(938), + #| Some(969), + #| Some(985), + #| Some(993), + #| Some(997), + #| Some(999), #|] ), ) @@ -47,7 +47,7 @@ test "shrink result" { debug_inspect( @shrink.Shrink::shrink(b).collect(), content=( - #|[Err(99), Err(97), Err(94), Err(88), Err(75), Err(50), Err(0)] + #|[Err(0), Err(50), Err(75), Err(88), Err(94), Err(97), Err(99)] ), ) debug_inspect( @@ -75,19 +75,19 @@ test "shrink array" { #| [1, 2, 3, 4, 6], #| [1, 2, 3, 4, 5], #| [0, 2, 3, 4, 5, 6], - #| [1, 1, 3, 4, 5, 6], #| [1, 0, 3, 4, 5, 6], - #| [1, 2, 2, 4, 5, 6], + #| [1, 1, 3, 4, 5, 6], #| [1, 2, 0, 4, 5, 6], - #| [1, 2, 3, 3, 5, 6], - #| [1, 2, 3, 2, 5, 6], + #| [1, 2, 2, 4, 5, 6], #| [1, 2, 3, 0, 5, 6], - #| [1, 2, 3, 4, 4, 6], - #| [1, 2, 3, 4, 3, 6], + #| [1, 2, 3, 2, 5, 6], + #| [1, 2, 3, 3, 5, 6], #| [1, 2, 3, 4, 0, 6], - #| [1, 2, 3, 4, 5, 5], - #| [1, 2, 3, 4, 5, 3], + #| [1, 2, 3, 4, 3, 6], + #| [1, 2, 3, 4, 4, 6], #| [1, 2, 3, 4, 5, 0], + #| [1, 2, 3, 4, 5, 3], + #| [1, 2, 3, 4, 5, 5], #|] ), ) @@ -103,8 +103,8 @@ test "shrink fixed array" { #| , #| , #| , - #| , #| , + #| , #| , #|] ), @@ -122,8 +122,8 @@ test "shrink array view" { #| , #| , #| , - #| , #| , + #| , #| , #|] ), @@ -139,7 +139,7 @@ test "shrink readonly array" { debug_inspect( candidates, content=( - #|[[], [1], [2], [1, 1], [0, 1], [2, 0]] + #|[[], [1], [2], [0, 1], [1, 1], [2, 0]] ), ) } @@ -151,14 +151,14 @@ test "shrink ref into fresh refs" { debug_inspect( candidates.map(candidate => candidate.val), content=( - #|[3, 2, 0] + #|[0, 2, 3] ), ) candidates[0].val = 99 debug_inspect( (input.val, candidates.map(candidate => candidate.val)), content=( - #|(4, [99, 2, 0]) + #|(4, [99, 2, 3]) ), ) } @@ -170,13 +170,13 @@ test "shrink tuple" { @shrink.Shrink::shrink(x).collect(), content=( #|[ - #| (119, true), - #| (117, true), - #| (113, true), - #| (105, true), - #| (90, true), - #| (60, true), #| (0, true), + #| (60, true), + #| (90, true), + #| (105, true), + #| (113, true), + #| (117, true), + #| (119, true), #| (120, false), #|] ), @@ -190,19 +190,19 @@ test "shrink 6-tuple" { @shrink.Shrink::shrink(x).collect(), content=( #|[ - #| (19, 'A', 30, true, true, true), - #| (18, 'A', 30, true, true, true), - #| (15, 'A', 30, true, true, true), - #| (10, 'A', 30, true, true, true), #| (0, 'A', 30, true, true, true), + #| (10, 'A', 30, true, true, true), + #| (15, 'A', 30, true, true, true), + #| (18, 'A', 30, true, true, true), + #| (19, 'A', 30, true, true, true), #| (20, 'a', 30, true, true, true), #| (20, 'b', 30, true, true, true), #| (20, 'c', 30, true, true, true), - #| (20, 'A', 29, true, true, true), - #| (20, 'A', 27, true, true, true), - #| (20, 'A', 23, true, true, true), - #| (20, 'A', 15, true, true, true), #| (20, 'A', 0, true, true, true), + #| (20, 'A', 15, true, true, true), + #| (20, 'A', 23, true, true, true), + #| (20, 'A', 27, true, true, true), + #| (20, 'A', 29, true, true, true), #| (20, 'A', 30, false, true, true), #| (20, 'A', 30, true, false, true), #| (20, 'A', 30, true, true, false), diff --git a/quickcheck/shrink/shrink.mbt b/quickcheck/shrink/shrink.mbt index 23083b0049..90d25ea7ff 100644 --- a/quickcheck/shrink/shrink.mbt +++ b/quickcheck/shrink/shrink.mbt @@ -47,20 +47,24 @@ impl Shrink with fn shrink(_a) { ///| pub impl Shrink for Int with fn shrink(x) { [| - ..[ for z = x / 2; (x - z).abs() < x.abs(); z = z / 2 => x - z ].rev_iter(), ..if x != 0 { [0] }, + ..[ + for z = x / 2; (x - z).abs() < x.abs(); z = z / 2 => x - z + ], |] } ///| pub impl Shrink for Int64 with fn shrink(x) { [| - ..[ for z = x / 2; (x - z).abs() < x.abs(); z = z / 2 => x - z ].rev_iter(), ..if x != 0 { [(0 : Int64)] }, + ..[ + for z = x / 2; (x - z).abs() < x.abs(); z = z / 2 => x - z + ], |] } @@ -81,17 +85,17 @@ pub impl Shrink for @bigint.BigInt with fn shrink(x) { let magnitude = if x < zero { -x } else { x } let bit_length = magnitude.bit_length() let negative = x < zero - let mut shift = bit_length - 1 + let mut shift = 1 let mut emit_zero = true Iter::new( () => { - if shift > 0 { - let delta = magnitude >> shift - shift -= 1 - Some(if negative { x + delta } else { x - delta }) - } else if emit_zero { + if emit_zero { emit_zero = false Some(zero) + } else if shift < bit_length { + let delta = magnitude >> shift + shift += 1 + Some(if negative { x + delta } else { x - delta }) } else { None } @@ -103,20 +107,24 @@ pub impl Shrink for @bigint.BigInt with fn shrink(x) { ///| pub impl Shrink for UInt with fn shrink(x) { [| - ..[ for z = x / 2; z > 0; z = z / 2 => x - z ].rev_iter(), ..if x != 0 { [(0 : UInt)] }, + ..[ + for z = x / 2; z > 0; z = z / 2 => x - z + ], |] } ///| pub impl Shrink for UInt64 with fn shrink(x) { [| - ..[ for z = x / 2; z > 0; z = z / 2 => x - z ].rev_iter(), ..if x != 0 { [(0 : UInt64)] }, + ..[ + for z = x / 2; z > 0; z = z / 2 => x - z + ], |] } @@ -133,10 +141,12 @@ pub impl Shrink for Bool with fn shrink(b) { pub impl Shrink for Byte with fn shrink(x) { let xi = x.to_int() [| - ..[ for z = xi / 2; z > 0; z = z / 2 => (xi - z).to_byte() ].rev_iter(), ..if xi != 0 { [Int::to_byte(0)] }, + ..[ + for z = xi / 2; z > 0; z = z / 2 => (xi - z).to_byte() + ], |] } diff --git a/quickcheck/shrink/shrink_test.mbt b/quickcheck/shrink/shrink_test.mbt index fc36aaf50a..526d799c06 100644 --- a/quickcheck/shrink/shrink_test.mbt +++ b/quickcheck/shrink/shrink_test.mbt @@ -17,7 +17,7 @@ test "shrink int" { debug_inspect( @shrink.Shrink::shrink(100).collect(), content=( - #|[99, 97, 94, 88, 75, 50, 0] + #|[0, 50, 75, 88, 94, 97, 99] ), ) debug_inspect( @@ -34,20 +34,20 @@ test "shrink int64" { @shrink.Shrink::shrink(10000L).collect(), content=( #|[ - #| 9999, - #| 9998, - #| 9996, - #| 9991, - #| 9981, - #| 9961, - #| 9922, - #| 9844, - #| 9688, - #| 9375, - #| 8750, - #| 7500, - #| 5000, #| 0, + #| 5000, + #| 7500, + #| 8750, + #| 9375, + #| 9688, + #| 9844, + #| 9922, + #| 9961, + #| 9981, + #| 9991, + #| 9996, + #| 9998, + #| 9999, #|] ), ) @@ -58,7 +58,7 @@ test "shrink int16" { debug_inspect( @shrink.Shrink::shrink((100 : Int16)).collect(), content=( - #|[99, 97, 94, 88, 75, 50, 0] + #|[0, 50, 75, 88, 94, 97, 99] ), ) } @@ -68,7 +68,7 @@ test "shrink uint16" { debug_inspect( @shrink.Shrink::shrink((100 : UInt16)).collect(), content=( - #|[99, 97, 94, 88, 75, 50, 0] + #|[0, 50, 75, 88, 94, 97, 99] ), ) } @@ -85,13 +85,9 @@ test "shrink arbitrary-precision bigint" { #|( #| 67, #| , - #| , + #| [0, 50000000000000000000, 75000000000000000000, 87500000000000000000]>, + #| , #|) ), ) @@ -113,12 +109,13 @@ test "shrink negative arbitrary-precision bigint" { #| 67, #| , - #| , + #| , #|) ), ) @@ -150,22 +147,22 @@ test "shrink uint" { @shrink.Shrink::shrink(37000U).collect(), content=( #|[ - #| 36999, - #| 36998, - #| 36996, - #| 36991, - #| 36982, - #| 36964, - #| 36928, - #| 36856, - #| 36711, - #| 36422, - #| 35844, - #| 34688, - #| 32375, - #| 27750, - #| 18500, #| 0, + #| 18500, + #| 27750, + #| 32375, + #| 34688, + #| 35844, + #| 36422, + #| 36711, + #| 36856, + #| 36928, + #| 36964, + #| 36982, + #| 36991, + #| 36996, + #| 36998, + #| 36999, #|] ), ) @@ -177,22 +174,22 @@ test "shrink uint64" { @shrink.Shrink::shrink((42000 : UInt64)).collect(), content=( #|[ - #| 41999, - #| 41998, - #| 41995, - #| 41990, - #| 41980, - #| 41959, - #| 41918, - #| 41836, - #| 41672, - #| 41344, - #| 40688, - #| 39375, - #| 36750, - #| 31500, - #| 21000, #| 0, + #| 21000, + #| 31500, + #| 36750, + #| 39375, + #| 40688, + #| 41344, + #| 41672, + #| 41836, + #| 41918, + #| 41959, + #| 41980, + #| 41990, + #| 41995, + #| 41998, + #| 41999, #|] ), ) @@ -204,7 +201,7 @@ test "shrink byte" { debug_inspect( s, content=( - #|[0xc7, 0xc5, 0xc2, 0xbc, 0xaf, 0x96, 0x64, 0x00] + #|[0x00, 0x64, 0x96, 0xaf, 0xbc, 0xc2, 0xc5, 0xc7] ), ) debug_inspect( @@ -286,19 +283,19 @@ test "shrink bytes through byte array" { #| , #| , #| , - #| , - #| , - #| , - #| , - #| , - #| , #| , - #| , - #| , - #| , - #| , - #| , + #| , + #| , + #| , + #| , + #| , + #| , #| , + #| , + #| , + #| , + #| , + #| , #|] ), ) @@ -327,7 +324,7 @@ test "shrink double: exact output for 3.5" { debug_inspect( @shrink.Shrink::shrink(3.5).collect(), content=( - #|[3, 2, 0, 3.4, 3.3, 3.1, 2.7, 1.8, 0] + #|[0, 2, 3, 1.8, 2.7, 3.1, 3.3, 3.4] ), ) } @@ -347,7 +344,7 @@ test "shrink double: negative" { debug_inspect( @shrink.Shrink::shrink(-5.5).collect(), content=( - #|[5.5, -5, -3, 0, -5.4, -5.2, -4.9, -4.2, -2.8, 0] + #|[5.5, 0, -3, -5, -2.8, -4.2, -4.9, -5.2, -5.4] ), ) } @@ -377,7 +374,7 @@ test "shrink double: small value" { debug_inspect( @shrink.Shrink::shrink(0.001).collect(), content=( - #|[0, 0] + #|[0] ), ) } diff --git a/quickcheck/shrink/utils.mbt b/quickcheck/shrink/utils.mbt index f9082c9f5d..a5d50f4b70 100644 --- a/quickcheck/shrink/utils.mbt +++ b/quickcheck/shrink/utils.mbt @@ -50,6 +50,6 @@ fn shrink_decimal(x : Double) -> Iter[Double] { [|m|] .concat(Shrink::shrink(m)) .map(n => n.to_double() / p) - .filter(y => y >= 0.0 && y < x) + .filter(y => y >= 0.0 && y < x && (p == 1.0 || y != 0.0)) }) } From 5f0dda5a107fc48bbc63a8a36a9b662a038cce4a Mon Sep 17 00:00:00 2001 From: mizchi Date: Tue, 25 Aug 2026 04:03:18 +0900 Subject: [PATCH 2/2] Lazily construct collection shrink candidates --- quickcheck/moon.pkg | 1 + quickcheck/shrink/collection.mbt | 19 ++++++++++------ quickcheck/shrink/composite.mbt | 2 +- quickcheck/shrink/utils.mbt | 1 + quickcheck/shrink_collection_bench_test.mbt | 25 +++++++++++++++++++++ 5 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 quickcheck/shrink_collection_bench_test.mbt diff --git a/quickcheck/moon.pkg b/quickcheck/moon.pkg index 813a46e88f..a6bb92913d 100644 --- a/quickcheck/moon.pkg +++ b/quickcheck/moon.pkg @@ -27,6 +27,7 @@ import { } import { + "moonbitlang/core/bench", "moonbitlang/core/float", "moonbitlang/core/double", } for "test" diff --git a/quickcheck/shrink/collection.mbt b/quickcheck/shrink/collection.mbt index 660981b072..c8539afea5 100644 --- a/quickcheck/shrink/collection.mbt +++ b/quickcheck/shrink/collection.mbt @@ -14,7 +14,7 @@ ///| pub impl[T : Shrink] Shrink for @list.List[T] with fn shrink(xs) { - let n = xs.length() + guard !xs.is_empty() else { return [||] } fn shr_sub_terms(lst : @list.List[T]) { match lst { Empty => [||] @@ -25,12 +25,17 @@ pub impl[T : Shrink] Shrink for @list.List[T] with fn shrink(xs) { } } - [ - for k = n; k > 0; k = k / 2 => k - ] - .iter() - .flat_map(k => removes_list(k, n, xs)) - .concat(shr_sub_terms(xs)) + [|xs.take(0)|].concat( + [|()|].flat_map(_ => { + let n = xs.length() + [ + for k = n / 2; k > 0; k = k / 2 => k + ] + .iter() + .flat_map(k => removes_list(k, n, xs)) + .concat([|()|].flat_map(_ => shr_sub_terms(xs))) + }), + ) } ///| diff --git a/quickcheck/shrink/composite.mbt b/quickcheck/shrink/composite.mbt index fbb052440d..717b4af88a 100644 --- a/quickcheck/shrink/composite.mbt +++ b/quickcheck/shrink/composite.mbt @@ -47,7 +47,7 @@ pub impl[X : Shrink] Shrink for Array[X] with fn shrink(xs) { ] .iter() .flat_map(k => removes_array(k, n, xs)) - .concat(shr_sub_terms(view)) + .concat([|()|].flat_map(_ => shr_sub_terms(view))) } ///| diff --git a/quickcheck/shrink/utils.mbt b/quickcheck/shrink/utils.mbt index a5d50f4b70..4f93598a7d 100644 --- a/quickcheck/shrink/utils.mbt +++ b/quickcheck/shrink/utils.mbt @@ -15,6 +15,7 @@ ///| fn[T] removes_array(k : Int, n : Int, xs : Array[T]) -> Iter[Array[T]] { guard k <= n else { [||] } + guard k < n else { [|[]|] } let xs2 = xs[:k].to_owned() let xs1 = xs[k:].to_owned() if xs1.is_empty() { diff --git a/quickcheck/shrink_collection_bench_test.mbt b/quickcheck/shrink_collection_bench_test.mbt new file mode 100644 index 0000000000..dd58e99658 --- /dev/null +++ b/quickcheck/shrink_collection_bench_test.mbt @@ -0,0 +1,25 @@ +// Copyright 2026 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +///| +test "bench shrink Array first candidate size=10000" (it : @bench.T) { + let input = Array::make(10000, ()) + it.bench(fn() { it.keep(@shrink.Shrink::shrink(input).next()) }) +} + +///| +test "bench shrink List first candidate size=10000" (it : @bench.T) { + let input : @list.List[Unit] = List(Array::make(10000, ())) + it.bench(fn() { it.keep(@shrink.Shrink::shrink(input).next()) }) +}