diff options
| author | Thijs Schreijer <thijs@thijsschreijer.nl> | 2026-02-13 14:49:50 +0100 |
|---|---|---|
| committer | Thijs Schreijer <thijs@thijsschreijer.nl> | 2026-02-13 14:49:50 +0100 |
| commit | f6f0c77995fcd62863a7f24cbb407a68d2277759 (patch) | |
| tree | e7713dd493c42404201f89993aa1513e224a53b6 /spec | |
| parent | dfd0d4b8ca3607ae39b1d2cbad4e3a7180dd6754 (diff) | |
| download | luasystem-f6f0c77995fcd62863a7f24cbb407a68d2277759.tar.gz luasystem-f6f0c77995fcd62863a7f24cbb407a68d2277759.tar.bz2 luasystem-f6f0c77995fcd62863a7f24cbb407a68d2277759.zip | |
feat(random): add rnd() to mimic math.random()
Matches Lua 5.4+ implementation. But uses crypto secure random data.
Diffstat (limited to 'spec')
| -rw-r--r-- | spec/02-random_spec.lua | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/spec/02-random_spec.lua b/spec/02-random_spec.lua index 23b6d95..db5d5ce 100644 --- a/spec/02-random_spec.lua +++ b/spec/02-random_spec.lua | |||
| @@ -44,4 +44,137 @@ describe("Random:", function() | |||
| 44 | 44 | ||
| 45 | end) | 45 | end) |
| 46 | 46 | ||
| 47 | |||
| 48 | |||
| 49 | describe("rnd()", function() | ||
| 50 | |||
| 51 | local has_math_type = type(math.type) == "function" | ||
| 52 | |||
| 53 | it("with no args returns a number in [0, 1)", function() | ||
| 54 | local v, err = system.rnd() | ||
| 55 | assert.is_nil(err) | ||
| 56 | assert.is_number(v) | ||
| 57 | assert.is_true(v >= 0 and v < 1) | ||
| 58 | end) | ||
| 59 | |||
| 60 | |||
| 61 | it("with no args returns different values on multiple calls", function() | ||
| 62 | local seen = {} | ||
| 63 | for _ = 1, 20 do | ||
| 64 | local v = system.rnd() | ||
| 65 | seen[v] = true | ||
| 66 | end | ||
| 67 | local count = 0 | ||
| 68 | for _ in pairs(seen) do count = count + 1 end | ||
| 69 | assert.is_true(count >= 2, "expected at least 2 distinct values in 20 calls") | ||
| 70 | end) | ||
| 71 | |||
| 72 | |||
| 73 | it("with one arg m returns integer in [1, m]", function() | ||
| 74 | for _ = 1, 50 do | ||
| 75 | local v, err = system.rnd(6) | ||
| 76 | assert.is_nil(err) | ||
| 77 | assert.is_true(v >= 1 and v <= 6 and math.floor(v) == v) | ||
| 78 | end | ||
| 79 | end) | ||
| 80 | |||
| 81 | |||
| 82 | it("matches math.random type behaviour for one-arg m when available", function() | ||
| 83 | if not has_math_type then | ||
| 84 | return | ||
| 85 | end | ||
| 86 | -- use math.random as ground truth for numeric type | ||
| 87 | local mval = math.random(6) | ||
| 88 | local rval = system.rnd(6) | ||
| 89 | assert.are.equal(math.type(mval), math.type(rval)) | ||
| 90 | end) | ||
| 91 | |||
| 92 | |||
| 93 | it("with one arg 1 always returns 1", function() | ||
| 94 | for _ = 1, 10 do | ||
| 95 | local v, err = system.rnd(1) | ||
| 96 | assert.is_nil(err) | ||
| 97 | assert.are.equal(1, v) | ||
| 98 | end | ||
| 99 | end) | ||
| 100 | |||
| 101 | |||
| 102 | it("with one arg 0 returns a full-range integer", function() | ||
| 103 | local v, err = system.rnd(0) | ||
| 104 | assert.is_nil(err) | ||
| 105 | assert.is_true(type(v) == "number" or (math.type and math.type(v) == "integer")) | ||
| 106 | assert.is_true(v >= -0x8000000000000000 and v <= 0x7fffffffffffffff) | ||
| 107 | end) | ||
| 108 | |||
| 109 | |||
| 110 | it("matches math.random type behaviour for arg 0 when math.random(0) is supported", function() | ||
| 111 | if not has_math_type then | ||
| 112 | return | ||
| 113 | end | ||
| 114 | local ok, mval = pcall(math.random, 0) | ||
| 115 | if not ok then | ||
| 116 | return -- older Lua where math.random(0) is not supported | ||
| 117 | end | ||
| 118 | local rval = system.rnd(0) | ||
| 119 | assert.are.equal(math.type(mval), math.type(rval)) | ||
| 120 | end) | ||
| 121 | |||
| 122 | |||
| 123 | it("with two args returns integer in [m, n]", function() | ||
| 124 | for _ = 1, 30 do | ||
| 125 | local v, err = system.rnd(10, 20) | ||
| 126 | assert.is_nil(err) | ||
| 127 | assert.is_true(v >= 10 and v <= 20 and math.floor(v) == v) | ||
| 128 | end | ||
| 129 | end) | ||
| 130 | |||
| 131 | |||
| 132 | it("matches math.random type behaviour for two-arg range when available", function() | ||
| 133 | if not has_math_type then | ||
| 134 | return | ||
| 135 | end | ||
| 136 | local mval = math.random(10, 20) | ||
| 137 | local rval = system.rnd(10, 20) | ||
| 138 | assert.are.equal(math.type(mval), math.type(rval)) | ||
| 139 | end) | ||
| 140 | |||
| 141 | |||
| 142 | it("with two args supports negative range", function() | ||
| 143 | for _ = 1, 30 do | ||
| 144 | local v, err = system.rnd(-5, 5) | ||
| 145 | assert.is_nil(err) | ||
| 146 | assert.is_true(v >= -5 and v <= 5 and math.floor(v) == v) | ||
| 147 | end | ||
| 148 | end) | ||
| 149 | |||
| 150 | |||
| 151 | it("with two equal args returns that value", function() | ||
| 152 | local v, err = system.rnd(7, 7) | ||
| 153 | assert.is_nil(err) | ||
| 154 | assert.are.equal(7, v) | ||
| 155 | end) | ||
| 156 | |||
| 157 | |||
| 158 | it("returns nil and error for empty interval (m > n)", function() | ||
| 159 | local v, err = system.rnd(10, 5) | ||
| 160 | assert.is_falsy(v) | ||
| 161 | assert.are.equal("interval is empty", err) | ||
| 162 | end) | ||
| 163 | |||
| 164 | |||
| 165 | it("returns nil and error for invalid one-arg (m < 1, m ~= 0)", function() | ||
| 166 | local v, err = system.rnd(-1) | ||
| 167 | assert.is_falsy(v) | ||
| 168 | assert.are.equal("interval is empty", err) | ||
| 169 | end) | ||
| 170 | |||
| 171 | |||
| 172 | it("returns nil and error for wrong number of arguments", function() | ||
| 173 | local v, err = system.rnd(1, 2, 3) | ||
| 174 | assert.is_falsy(v) | ||
| 175 | assert.are.equal("wrong number of arguments", err) | ||
| 176 | end) | ||
| 177 | |||
| 178 | end) | ||
| 179 | |||
| 47 | end) | 180 | end) |
