File 7311-common_test-Add-quickcheck-variant-of-ct_proper_ext.patch of Package erlang

From 09755aa18a33e38be4dc5ccd6adabba8bfcfe081 Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Fri, 27 Feb 2026 16:51:31 +0100
Subject: [PATCH 1/6] common_test: Add quickcheck variant of ct_proper_ext

To make it possible to use Quickcheck instead of proper.
---
 lib/common_test/include/ct_property_test.hrl  |  19 ++
 lib/common_test/proper_ext/Makefile           |   3 +-
 .../proper_ext/ct_quickcheck_ext.erl          | 167 ++++++++++++++++++
 lib/common_test/src/ct_property_test.erl      |   8 +-
 .../test/property_test/ct_prop.erl            |  22 +++
 5 files changed, 214 insertions(+), 5 deletions(-)
 create mode 100644 lib/common_test/proper_ext/ct_quickcheck_ext.erl

diff --git a/lib/common_test/include/ct_property_test.hrl b/lib/common_test/include/ct_property_test.hrl
index a0b9e45b92..ffa8492f11 100644
--- a/lib/common_test/include/ct_property_test.hrl
+++ b/lib/common_test/include/ct_property_test.hrl
@@ -24,13 +24,32 @@
 -ifndef(CT_PROPERTY_TEST_HRL).
   -define(CT_PROPERTY_TEST_HRL, true).
 
+  -define(CT_BYTE(), choose(0, 255)).
+  -define(CT_NEG_INT(), ?LET(N, int(), -abs(N)-1)).
+  -define(CT_RANGE(Min,Max), choose(Min, Max)).
+  -define(CT_INT(Min,Max), choose(Min, Max)).
+
   -ifdef(EQC).
     -define(MOD_eqc, eqc).
     -include_lib("eqc/include/eqc.hrl").
+    -define(CT_SAFE_ANY(), ct_quickcheck_ext:safe_any()).
+    -define(CT_SAFE_ATOM(), ct_quickcheck_ext:safe_atom()).
+    -define(CT_SAFE_LIST(), ct_quickcheck_ext:safe_list()).
+    -define(CT_SAFE_MAP(), ct_quickcheck_ext:safe_map()).
+    -define(CT_SAFE_TERM(), ct_quickcheck_ext:safe_term()).
+    -define(CT_SAFE_TUPLE(), ct_quickcheck_ext:safe_tuple()).
+    -define(CT_EXISTING_ATOM(), ct_quickcheck_ext:existing_atom()).
   -else.
     -ifdef(PROPER).
       -define(MOD_eqc, proper).
       -include_lib("proper/include/proper.hrl").
+      -define(CT_SAFE_ANY(), ct_proper_ext:safe_any()).
+      -define(CT_SAFE_ATOM(), ct_proper_ext:safe_atom()).
+      -define(CT_SAFE_LIST(), ct_proper_ext:safe_list()).
+      -define(CT_SAFE_MAP(), ct_proper_ext:safe_map()).
+      -define(CT_SAFE_TERM(), ct_proper_ext:safe_term()).
+      -define(CT_SAFE_TUPLE(), ct_proper_ext:safe_tuple()).
+      -define(CT_EXISTING_ATOM(), ct_proper_ext:existing_atom()).
     -else.
       -ifdef(TRIQ).
         -define(MOD_eqc, triq).
diff --git a/lib/common_test/proper_ext/Makefile b/lib/common_test/proper_ext/Makefile
index 28a1b6cf3d..e4bf1dc323 100644
--- a/lib/common_test/proper_ext/Makefile
+++ b/lib/common_test/proper_ext/Makefile
@@ -39,7 +39,8 @@ PROPEREXTDIR = $(RELEASE_PATH)/lib/common_test-$(VSN)/proper_ext
 EBIN=.
 
 MODULES= \
-	ct_proper_ext
+	ct_proper_ext \
+	ct_quickcheck_ext
 
 TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
 TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
diff --git a/lib/common_test/proper_ext/ct_quickcheck_ext.erl b/lib/common_test/proper_ext/ct_quickcheck_ext.erl
new file mode 100644
index 0000000000..9162662b63
--- /dev/null
+++ b/lib/common_test/proper_ext/ct_quickcheck_ext.erl
@@ -0,0 +1,167 @@
+%%
+%% %CopyrightBegin%
+%%
+%% SPDX-License-Identifier: Apache-2.0
+%%
+%% Copyright Ericsson AB 2023-2025. All Rights Reserved.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%% For internal use only.
+%%
+%% Some generators of the QuickCheck framework used by OTP for property tests
+%% create atoms at random, ie from random strings, and are therefore likely
+%% to exhaust the atom table.
+%%
+%% This module provides additional variants of these generators which do
+%% not create new atoms but pick from the already existing atoms.
+%%
+%% Other than in QuickCheck, the respective atom generators provided by this module
+%% do not shrink.
+
+-module(ct_quickcheck_ext).
+-moduledoc false.
+
+-export([existing_atom/0]).
+-export([safe_any/0]).
+-export([safe_atom/0]).
+-export([safe_list/0]).
+-export([safe_map/0]).
+-export([safe_term/0]).
+-export([safe_tuple/0]).
+
+%% Atomlimit-safe variant of `eqc_gen:list()'
+-spec safe_list() -> eqc_gen:gen(list()).
+safe_list() ->
+    eqc_gen:list(safe_any()).
+
+
+%% Atomlimit-safe variant of `eqc_gen:map()'
+-spec safe_map() -> eqc_gen:gen(map()).
+safe_map() ->
+    eqc_gen:map(safe_any(), safe_any()).
+
+
+%% Atomlimit-safe variant of tuple generator
+-spec safe_tuple() -> eqc_gen:gen(tuple()).
+safe_tuple() ->
+    eqc_gen:bind(eqc_gen:list(safe_any()), fun(L) -> list_to_tuple(L) end).
+
+
+%% Atomlimit-safe atom generator.
+-spec existing_atom() -> eqc_gen:gen(atom()).
+existing_atom() ->
+    existing_atom(atom_count()).
+
+existing_atom(N) ->
+    eqc_gen:noshrink(
+      eqc_gen:bind(eqc_gen:choose(0, N - 1), fun(X) -> get_existing_atom(X) end)).
+
+-define(ATOM_TERM_BIN(Index), <<131, 75, Index:24>>).
+get_existing_atom(Index) ->
+    case binary_to_term(?ATOM_TERM_BIN(Index)) of
+        '' ->
+            '';
+        Atom ->
+            case hd(atom_to_list(Atom)) of
+                $$ when Index > 0 ->
+                    get_existing_atom(Index - 1);
+                $$ ->
+                    '';
+                _ ->
+                    Atom
+            end
+    end.
+
+%% Atomlimit-safe atom generator.
+%% Like `existing_atom()', but also emphasizes some common atoms
+%% like `undefined', `false', `ok' etc
+-spec safe_atom() -> eqc_gen:gen(atom()).
+safe_atom() ->
+    safe_atom(atom_count()).
+
+safe_atom(N) ->
+    eqc_gen:oneof([eqc_gen:oneof(['', true, false, ok,
+                                   error, undefined,
+                                   infinity, 'ätöm',
+                                   '原子', '_', '"',
+                                   '\'', '\\', '+', '-',
+                                   '*', '/', '(', ')',
+                                   '[', ']', '{', '}',
+                                   '#']),
+                   existing_atom(N)]).
+
+%% Atomlimit-safe variant of term generator.
+%% Alias for `safe_any/0'.
+-spec safe_term() -> eqc_gen:gen(term()).
+safe_term() ->
+    safe_any().
+
+%% Atomlimit-safe variant of `eqc_gen:any()'.
+-spec safe_any() -> eqc_gen:gen(term()).
+safe_any() ->
+    N = atom_count(),
+    eqc_gen:sized(fun(Size) -> safe_any(N, Size) end).
+
+%% I have simplified 'safe_any/2' here, it is missing the distribution function that
+%% ct_proper_ext have.
+%% The solution in Proper uses a lot of random calls,
+%% but that didn't work with Quickcheck combined with functionX generators
+%% that uses safe_any() as type.
+%% My guess is that Quickcheck is more lazy and funs becomes non-functional
+%% and returns random output on the same input, when rand is invoked.
+
+safe_any(N, 0) ->
+    eqc_gen:oneof([safe_atom(N),
+                   eqc_gen:int(),
+                   eqc_gen:real()]);
+
+safe_any(N, Size) ->
+    eqc_gen:frequency(
+      [{25, eqc_gen:bind(eqc_gen:choose(0, Size), make_tuple(N, Size))},
+       {25, eqc_gen:bind(eqc_gen:choose(0, Size), make_list(N, Size))},
+       {12, eqc_gen:resize(Size, eqc_gen:bitstring())},
+       {38, safe_any(N,0)}]).
+
+make_list(N, Size) ->
+    fun(X) ->
+            case X of
+                0 ->
+                    [];
+                1 ->
+                    [eqc_gen:lazy(fun() -> safe_any(N, Size - 1) end)];
+                _ ->
+                    Content = eqc_gen:lazy(fun() -> safe_any(N, Size div X) end),
+                    eqc_gen:list(X, Content)
+            end
+    end.
+
+make_tuple(N, Size) ->
+    fun(X) ->
+            case X of
+                0 ->
+                    {};
+                1 ->
+                    {eqc_gen:lazy(fun() -> safe_any(N, Size - 1) end)};
+                _ ->
+                    Content = eqc_gen:lazy(fun() -> safe_any(N, Size div X) end),
+                    list_to_tuple(lists:duplicate(X, Content))
+            end
+    end.
+
+%% Number of currently existing atoms
+atom_count() ->
+    erlang:system_info(atom_count).
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index e0f86e65b7..6e1051456b 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -204,10 +204,10 @@ init_tool(Config) ->
             {skip, "No property testing tool found"}
     end.
 
-init_tool_extensions(proper) ->
-    ProperExtDir = code:lib_dir(common_test, proper_ext),
-    true = code:add_patha(ProperExtDir),
-    ct:log("Added ~ts to code path~n", [ProperExtDir]),
+init_tool_extensions(ToolModule) when ToolModule =:= eqc; ToolModule =:= proper ->
+    ExtDir = filename:join(code:lib_dir(common_test), proper_ext),
+    true = code:add_patha(ExtDir),
+    ct:log("Added ~ts to code path~n", [ExtDir]),
     ok;
 init_tool_extensions(_) ->
     ok.
diff --git a/lib/common_test/test/property_test/ct_prop.erl b/lib/common_test/test/property_test/ct_prop.erl
index 67ab3f3e6b..8f1cac622f 100644
--- a/lib/common_test/test/property_test/ct_prop.erl
+++ b/lib/common_test/test/property_test/ct_prop.erl
@@ -1,3 +1,25 @@
+%%
+%% %CopyrightBegin%
+%%
+%% SPDX-License-Identifier: Apache-2.0
+%%
+%% Copyright Ericsson AB 2003-2025. All Rights Reserved.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
 -module(ct_prop).
 -export([prop_sort/0]).
 
-- 
2.51.0

openSUSE Build Service is sponsored by