Repository: mitsuhiko/minijinja
Pull request #696 (2025-02-02 13:56:15)

Switch to basic dependency free xorrand for contrib
This is good enough for template rendering purposes.

Seeding has the same issues as before.  I filed #695


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a28599..1166682 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ All notable changes to MiniJinja are documented here.
 ## 2.9.0
 
 - Raise MSRV to 1.70.
+- The contrib crate now uses a basic xorrand implementation instead
+  of depending on all of the `rand` module.  #696
 
 ## 2.8.0
 
diff --git a/Cargo.lock b/Cargo.lock
index 3d87f36..7d5e390 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,7 +49,7 @@ dependencies = [
  "mime",
  "percent-encoding",
  "pin-project-lite",
- "rand 0.8.5",
+ "rand",
  "sha1",
  "smallvec",
  "tokio",
@@ -215,10 +215,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
 dependencies = [
  "cfg-if",
- "getrandom 0.2.15",
+ "getrandom",
  "once_cell",
  "version_check",
- "zerocopy 0.7.35",
+ "zerocopy",
 ]
 
 [[package]]
@@ -1173,19 +1173,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.13.3+wasi-0.2.2",
- "windows-targets",
+ "wasi",
 ]
 
 [[package]]
@@ -1967,7 +1955,6 @@ dependencies = [
  "chrono",
  "insta",
  "minijinja",
- "rand 0.9.0",
  "serde",
  "similar-asserts",
  "textwrap",
@@ -2020,7 +2007,7 @@ dependencies = [
  "hermit-abi 0.3.9",
  "libc",
  "log",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
  "windows-sys 0.52.0",
 ]
 
@@ -2262,7 +2249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
 dependencies = [
  "phf_shared",
- "rand 0.8.5",
+ "rand",
 ]
 
 [[package]]
@@ -2351,7 +2338,7 @@ version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
 dependencies = [
- "zerocopy 0.7.35",
+ "zerocopy",
 ]
 
 [[package]]
@@ -2471,19 +2458,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
- "rand_chacha 0.3.1",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
-dependencies = [
- "rand_chacha 0.9.0",
- "rand_core 0.9.0",
- "zerocopy 0.8.14",
+ "rand_chacha",
+ "rand_core",
 ]
 
 [[package]]
@@ -2493,17 +2469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
-dependencies = [
- "ppv-lite86",
- "rand_core 0.9.0",
+ "rand_core",
 ]
 
 [[package]]
@@ -2512,17 +2478,7 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom 0.2.15",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
-dependencies = [
- "getrandom 0.3.1",
- "zerocopy 0.8.14",
+ "getrandom",
 ]
 
 [[package]]
@@ -3084,7 +3040,7 @@ dependencies = [
  "percent-encoding",
  "pest",
  "pest_derive",
- "rand 0.8.5",
+ "rand",
  "regex",
  "serde",
  "serde_json",
@@ -3478,15 +3434,6 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
-[[package]]
-name = "wasi"
-version = "0.13.3+wasi-0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
-dependencies = [
- "wit-bindgen-rt",
-]
-
 [[package]]
 name = "wasm-bindgen"
 version = "0.2.95"
@@ -3661,15 +3608,6 @@ dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "wit-bindgen-rt"
-version = "0.33.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
-dependencies = [
- "bitflags 2.8.0",
-]
-
 [[package]]
 name = "write16"
 version = "1.0.0"
@@ -3728,16 +3666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
  "byteorder",
- "zerocopy-derive 0.7.35",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.8.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
-dependencies = [
- "zerocopy-derive 0.8.14",
+ "zerocopy-derive",
 ]
 
 [[package]]
@@ -3751,17 +3680,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "zerofrom"
 version = "0.1.4"
diff --git a/minijinja-contrib/Cargo.toml b/minijinja-contrib/Cargo.toml
index ad4d860..d5d56af 100644
--- a/minijinja-contrib/Cargo.toml
+++ b/minijinja-contrib/Cargo.toml
@@ -20,14 +20,13 @@ default = []
 pycompat = ["minijinja/builtins"]
 datetime = ["time"]
 timezone = ["time-tz"]
-rand = ["dep:rand"]
+rand = []
 wordcount = ["unicode_categories"]
 wordwrap = ["textwrap"]
 unicode_wordwrap = ["wordwrap", "textwrap/unicode-linebreak", "textwrap/unicode-width"]
 
 [dependencies]
 minijinja = { version = "2.7.0", path = "../minijinja", default-features = false }
-rand = { version = "0.9.0", optional = true, default-features = false, features = ["std", "small_rng", "os_rng"] }
 serde = "1.0.164"
 textwrap = { version = "0.16.1", optional = true, default-features = false, features = ["smawk"] }
 time = { version = "0.3.35", optional = true, features = ["serde", "formatting", "parsing"] }
diff --git a/minijinja-contrib/src/filters/mod.rs b/minijinja-contrib/src/filters/mod.rs
index 17e9c88..33270a0 100644
--- a/minijinja-contrib/src/filters/mod.rs
+++ b/minijinja-contrib/src/filters/mod.rs
@@ -77,11 +77,10 @@ pub fn pluralize(
 pub fn random(state: &minijinja::State, seq: &Value) -> Result<Value, Error> {
     use crate::globals::get_rng;
     use minijinja::value::ValueKind;
-    use rand::Rng;
 
     if matches!(seq.kind(), ValueKind::Seq | ValueKind::String) {
         let len = seq.len().unwrap_or(0);
-        let idx = get_rng(state).random_range(0..len);
+        let idx = get_rng(state).next_usize(len);
         seq.get_item_by_index(idx)
     } else {
         Err(Error::new(
diff --git a/minijinja-contrib/src/globals.rs b/minijinja-contrib/src/globals.rs
index e4fd97e..c070d82 100644
--- a/minijinja-contrib/src/globals.rs
+++ b/minijinja-contrib/src/globals.rs
@@ -125,18 +125,15 @@ pub fn joiner(sep: Option<Value>) -> Value {
 
 /// Returns the rng for the state
 #[cfg(feature = "rand")]
-pub(crate) fn get_rng(state: &State) -> rand::rngs::SmallRng {
-    use rand::rngs::SmallRng;
-    use rand::SeedableRng;
-
-    if let Some(seed) = state
-        .lookup("RAND_SEED")
-        .and_then(|x| u64::try_from(x).ok())
-    {
-        SmallRng::seed_from_u64(seed)
-    } else {
-        SmallRng::from_os_rng()
-    }
+pub(crate) fn get_rng(state: &State) -> crate::rand::XorShiftRng {
+    // XXX: we have no way to stash the rng away today which renders this
+    // feature rather useless.  Repeated calls to randrange etc. will
+    // always yield the same number.
+    crate::rand::XorShiftRng::new(
+        state
+            .lookup("RAND_SEED")
+            .and_then(|x| u64::try_from(x).ok()),
+    )
 }
 
 /// Returns a random number in a given range.
@@ -150,14 +147,12 @@ pub(crate) fn get_rng(state: &State) -> rand::rngs::SmallRng {
 #[cfg(feature = "rand")]
 #[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
 pub fn randrange(state: &State, n: i64, m: Option<i64>) -> i64 {
-    use rand::Rng;
-
     let (lower, upper) = match m {
         None => (0, n),
         Some(m) => (n, m),
     };
 
-    get_rng(state).random_range(lower..upper)
+    get_rng(state).random_range(lower, upper)
 }
 
 /// Generates a random lorem ipsum.
@@ -178,9 +173,6 @@ pub fn lipsum(
     n: Option<usize>,
     kwargs: minijinja::value::Kwargs,
 ) -> Result<Value, Error> {
-    use rand::seq::IndexedRandom;
-    use rand::Rng;
-
     #[rustfmt::skip]
     const LIPSUM_WORDS: &[&str] = &[
         "a", "ac", "accumsan", "ad", "adipiscing", "aenean", "aliquam",
@@ -230,14 +222,14 @@ pub fn lipsum(
         let mut last_fullstop = 0;
         let mut last = "";
 
-        for idx in 0..rng.random_range(min..max) {
+        for idx in 0..rng.random_range(min as i64, max as i64) {
             if idx > 0 {
                 rv.push(' ');
             } else if html {
                 rv.push_str("<p>");
             }
             let word = loop {
-                let word = LIPSUM_WORDS.choose(&mut rng).copied().unwrap_or("");
+                let word = LIPSUM_WORDS[rng.next_usize(LIPSUM_WORDS.len())];
                 if word != last {
                     last = word;
                     break word;
@@ -258,7 +250,7 @@ pub fn lipsum(
                 rv.push_str(word);
             }
 
-            if idx - last_fullstop > rng.random_range(10..20) {
+            if idx - last_fullstop > rng.random_range(10, 20) {
                 rv.push('.');
                 last_fullstop = idx;
                 next_capitalized = true;
diff --git a/minijinja-contrib/src/lib.rs b/minijinja-contrib/src/lib.rs
index d1a9e11..8e6f3da 100644
--- a/minijinja-contrib/src/lib.rs
+++ b/minijinja-contrib/src/lib.rs
@@ -24,6 +24,9 @@ pub mod filters;
 /// Globals
 pub mod globals;
 
+#[cfg(feature = "rand")]
+mod rand;
+
 /// Registers all features of this crate with an [`Environment`].
 ///
 /// All the filters that are available will be added, same with global
diff --git a/minijinja-contrib/src/rand.rs b/minijinja-contrib/src/rand.rs
new file mode 100644
index 0000000..2af40e1
--- /dev/null
+++ b/minijinja-contrib/src/rand.rs
@@ -0,0 +1,48 @@
+use std::collections::hash_map::RandomState;
+use std::hash::{BuildHasher, Hasher};
+use std::sync::Arc;
+
+use minijinja::value::{Object, ObjectRepr};
+
+#[derive(Debug)]
+pub struct XorShiftRng {
+    seed: u64,
+}
+
+impl Object for XorShiftRng {
+    fn repr(self: &Arc<Self>) -> ObjectRepr {
+        ObjectRepr::Plain
+    }
+}
+
+impl XorShiftRng {
+    pub fn new(seed: Option<u64>) -> XorShiftRng {
+        XorShiftRng {
+            seed: match seed {
+                Some(seed) => seed,
+                None => RandomState::new().build_hasher().finish(),
+            },
+        }
+    }
+
+    pub fn next(&mut self) -> u64 {
+        let mut rv = self.seed;
+        rv ^= rv << 13;
+        rv ^= rv >> 7;
+        rv ^= rv << 17;
+        self.seed = rv;
+        rv
+    }
+
+    pub fn next_usize(&mut self, max: usize) -> usize {
+        (self.random() * max as f64) as usize
+    }
+
+    pub fn random(&mut self) -> f64 {
+        (self.next() as f64) / (u64::MAX as f64)
+    }
+
+    pub fn random_range(&mut self, lower: i64, upper: i64) -> i64 {
+        (self.random() * (upper - lower) as f64) as i64 + lower
+    }
+}


Test patch:
diff --git a/minijinja-contrib/tests/filters.rs b/minijinja-contrib/tests/filters.rs
index 6e44c31..da15d67 100644
--- a/minijinja-contrib/tests/filters.rs
+++ b/minijinja-contrib/tests/filters.rs
@@ -103,8 +103,8 @@ fn test_random() {
     let mut env = Environment::new();
     env.add_filter("random", random);
 
-    insta::assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ [1, 2, 3, 4]|random }}"), @"4");
-    insta::assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ 'HelloWorld'|random }}"), @"l");
+    insta::assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ [1, 2, 3, 4]|random }}"), @"1");
+    insta::assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ 'HelloWorld'|random }}"), @"H");
 }
 
 #[test]
diff --git a/minijinja-contrib/tests/globals.rs b/minijinja-contrib/tests/globals.rs
index 6d8a046..b2f053c 100644
--- a/minijinja-contrib/tests/globals.rs
+++ b/minijinja-contrib/tests/globals.rs
@@ -54,21 +54,21 @@ fn test_lispum() {
     env.add_function("lipsum", lipsum);
 
     assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ lipsum(5) }}"), @r###"
-    Felis platea netus nisl sociosqu ssociis at morbi ante lobortis ssociis mi cubilia sociosqu ssociis. Nunc etiam posuere interdum sagittis dapibus nibh ipsum neque elementum magna scelerisque diam dictum arcu ssociis sociosqu nunc. Scelerisque ad sociosqu convallis leo facilisi felis in et id habitant orci consequat nisl mi. Porttitor dictumst hendrerit egestas eleifend ssociis lacus pellentesque nonummy eu ssociis facilisis justo ssociis vsociis felis odio. Lacinia sociosqu litora lectus pede elit curae dolor adipiscing quam sociosqu hac eros amet a enim. Inceptos fermentum ornare dis.
+    Nulla curae morbi nec gravida scelerisque habitant facilisi eros lectus molestie mattis neque dignissim. Convallis per ssociis erat ipsum pellentesque.
 
-    Enim sociosqu praesent ipsum cubilia sociosqu commodo aliquet montes pellentesque sociosqu dapibus interdum. Elementum enim natoque quisque inceptos pede sociosqu sem sociosqu consectetuer lectus est. Sociis nisl sit hendrerit molestie parturient magna id orci erat proin phasellus ante sociosqu ssociis egestas. Posuere fames iaculis libero fermentum neque integer euismod fusce et euismod eu ac. Amet pellentesque per nunc sociis eleifend massa fames fermentum purus non. Ssociis feugiat dignissim nostra dis commodo sociosqu gravida nullam quisque sit et malesuada ante sociosqu. Lectus etiam justo phasellus proin ssoincidusociis sociosqu facilisi nec s placerat netus ssociis aptent. Sociosqu sapien.
+    Imperdiet enim egestas feugiat adipiscing sociosqu vulputate malesuada fames elit massa arcu eleifend porta morbi lectus. Aenean metus risus elit pede nec morbi hendrerit ssociis natoque gravida montes ssociis ante gravida dignissim. Congue sapien augue sociosqu ssociis aptent id ridiculus eu sed imperdiet enim aliquam hendrerit rutrum. Quisque bibendum neque ssociis porta mauris ssociis sociis facilisi gravida proin metus imperdiet luctus. Nam natoque pulvinar dolor sociosqu aptent at sociosqu placerat malesuada placerat et. Fusce aptent hymenaeos mauris leo elit morbi proin cum consectetuer cras ssociis lacus maecenas. Ad potenti duis ssociis ante hymenaeos mi dictum ligula dictum.
 
-    Magnis ssociis per commodo curabitur sociosqu platea condimentum enim sociosqu nullam litora proin molestie fusce. Molestie sociosqu ssociis lectus ligula.
+    Ridiculus ssociis ac habitasse ssociis maecenas lacinia diam faucibus porta diam magna. Mus laoreet mollis sociosqu ssociis mus mollis praesent ssociis molestie habitant inceptos. Sociosqu class congue eu luctus rhoncus dolor sem natoque mattis hymenaeos fusce nunc. Egestas habitant cum pulvinar parturient ssociis sociosqu metus mus aliquam libero nec platea curabitur orci nisi. Purus dapibus nunc arcu donec cursus ornare dui in porttitor potenti a nascetur. S nisi posuere pretium hac lacinia pulvinar senectus platea dis mattis semper condimentum convallis cursus dis. Mattis ssociis inceptos duis.
 
-    Sapien faucibus senectus convallis augue fames habitant morbi montes potenti nisi pretium mauris. Ante ssociis purus neque bibendum faucibus neque aliquam amet ssociis congue maecenas dolor dignissim. Habitasse nostra phasellus imperdiet id porta litora blandit in sed lacinia. Quam ssoincidusociis sociosqu massa proin dolor montes imperdiet cum sociosqu bibendum auctor.
+    Platea donec vsociis ssociis aliquet non ssociis sit placerat nostra lacus habitant ssociis. Phasellus hymenaeos arcu sit magnis dis at nisi cras curabitur sociosqu eget sociis cras. Aenean duis iaculis platea donec sociosqu lacus pretium ssociis pellentesque risus ssociis nam nonummy. Adipiscing rutrum in commodo non hac vsociis etiam vulputate sapien sociosqu potenti aliquam. Quis cras nostra senectus amet adipiscing duis aliquam semper etiam elementum. Ssociis mi in laoreet at sociis ssociis.
 
-    Cras natoque felis nostra ssociis arcu sociosqu scelerisque ssociis eros placerat proin rhoncus sociis est ssociis dapibus condimentum sed. Quisque et class placerat pharetra malesuada enim potenti fermentum natoque dolor risus auctor sociosqu nec risus ssociis sociosqu. Et bibendum egestas gravida pellentesque montes dapibus s donec hendrerit mollis et sit nibh amet. Cursus pretium molestie ssociis ridiculus convallis sociosqu sociis sagittis fermentum gravida quisque nostra. Ante scelerisque sociosqu non magnis ssociis lacinia feugiat risus erat risus.
+    Neque eros ssociis faucibus euismod est interdum nam quis condimentum dis natoque a ssociis nec. Duis erat mollis cubilia faucibus rhoncus pellentesque laoreet commodo mi imperdiet pede ssociis. Parturient lobortis ssociis quam lectus nec ac dui maecenas orci netus fringilla magnis curabitur justo. Sed porta phasellus molestie cubilia nunc luctus platea mattis platea nullam elementum cursus ornare. Ssociis scelerisque.
     "###);
 
     assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ lipsum(2, html=true) }}"), @r###"
-    <p>Felis platea netus nisl sociosqu ssociis at morbi ante lobortis ssociis mi cubilia sociosqu ssociis. Nunc etiam posuere interdum sagittis dapibus nibh ipsum neque elementum magna scelerisque diam dictum arcu ssociis sociosqu nunc. Scelerisque ad sociosqu convallis leo facilisi felis in et id habitant orci consequat nisl mi. Porttitor dictumst hendrerit egestas eleifend ssociis lacus pellentesque nonummy eu ssociis facilisis justo ssociis vsociis felis odio. Lacinia sociosqu litora lectus pede elit curae dolor adipiscing quam sociosqu hac eros amet a enim. Inceptos fermentum ornare dis.</p>
+    <p>Nulla curae morbi nec gravida scelerisque habitant facilisi eros lectus molestie mattis neque dignissim. Convallis per ssociis erat ipsum pellentesque.</p>
 
-    <p>Enim sociosqu praesent ipsum cubilia sociosqu commodo aliquet montes pellentesque sociosqu dapibus interdum. Elementum enim natoque quisque inceptos pede sociosqu sem sociosqu consectetuer lectus est. Sociis nisl sit hendrerit molestie parturient magna id orci erat proin phasellus ante sociosqu ssociis egestas. Posuere fames iaculis libero fermentum neque integer euismod fusce et euismod eu ac. Amet pellentesque per nunc sociis eleifend massa fames fermentum purus non. Ssociis feugiat dignissim nostra dis commodo sociosqu gravida nullam quisque sit et malesuada ante sociosqu. Lectus etiam justo phasellus proin ssoincidusociis sociosqu facilisi nec s placerat netus ssociis aptent. Sociosqu sapien.</p>
+    <p>Imperdiet enim egestas feugiat adipiscing sociosqu vulputate malesuada fames elit massa arcu eleifend porta morbi lectus. Aenean metus risus elit pede nec morbi hendrerit ssociis natoque gravida montes ssociis ante gravida dignissim. Congue sapien augue sociosqu ssociis aptent id ridiculus eu sed imperdiet enim aliquam hendrerit rutrum. Quisque bibendum neque ssociis porta mauris ssociis sociis facilisi gravida proin metus imperdiet luctus. Nam natoque pulvinar dolor sociosqu aptent at sociosqu placerat malesuada placerat et. Fusce aptent hymenaeos mauris leo elit morbi proin cum consectetuer cras ssociis lacus maecenas. Ad potenti duis ssociis ante hymenaeos mi dictum ligula dictum.</p>
     "###);
 }
 
@@ -80,6 +80,6 @@ fn test_randrange() {
     let mut env = Environment::new();
     env.add_function("randrange", randrange);
 
-    assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ randrange(10) }}"), @"8");
-    assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ randrange(-50, 50) }}"), @"31");
+    assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ randrange(10) }}"), @"0");
+    assert_snapshot!(render!(in env, r"{% set RAND_SEED = 42 %}{{ randrange(-50, 50) }}"), @"-50");
 }