Skip to content

Commit 7c00ec7

Browse files
committed
Add simple assertions library and tests
1 parent ffee20e commit 7c00ec7

File tree

8 files changed

+265
-16
lines changed

8 files changed

+265
-16
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ Cargo.lock
99

1010
# These are backup files generated by rustfmt
1111
**/*.rs.bk
12+
13+
# VScode
14+
.vscode/

Cargo.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
[package]
22
name = "mlua-stdlib"
33
description = "Standard library bindings for mlua"
4-
version = "0.0.0"
5-
edition = "2021"
4+
version = "0.1.0"
5+
edition = "2024"
66
license = "MIT"
77

88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

10+
[features]
11+
lua51 = ["mlua/lua51"]
12+
lua52 = ["mlua/lua52"]
13+
lua53 = ["mlua/lua53"]
14+
lua54 = ["mlua/lua54"]
15+
luau = ["mlua/luau"]
16+
1017
[dependencies]
18+
mlua = { version = "0.11" }
19+
20+
[dev-dependencies]
21+
mlua = { version = "0.11", features = ["macros"] }

lua/assertions.lua

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
local assertions = {}
2+
3+
function assertions.assert_eq(left, right, message)
4+
if left ~= right then
5+
if message ~= nil then
6+
message = string.format("assertion `left == right` failed: %s", tostring(message))
7+
else
8+
message = "assertion `left == right` failed!"
9+
end
10+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)))
11+
end
12+
end
13+
14+
function assertions.assert_ne(left, right, message)
15+
if left == right then
16+
if message ~= nil then
17+
message = string.format("assertion `left ~= right` failed: %s", tostring(message))
18+
else
19+
message = "assertion `left ~= right` failed!"
20+
end
21+
error(string.format("%s\n left: %s\n right: %s", message, tostring(left), tostring(right)))
22+
end
23+
end
24+
25+
local function next_level(level, k)
26+
if type(k) == "string" then
27+
return level .. '["' .. k .. '"]'
28+
else
29+
return level .. "[" .. tostring(k) .. "]"
30+
end
31+
end
32+
33+
local function deepcmp(left, right, level, visited, report_cb)
34+
if type(left) ~= type(right) then
35+
report_cb(left, right, level)
36+
return false
37+
end
38+
if rawequal(left, right) then
39+
return true
40+
end
41+
if type(left) ~= "table" then
42+
report_cb(left, right, level)
43+
return false
44+
end
45+
46+
-- Prevent recursion
47+
if visited[left] or visited[right] then
48+
report_cb(left, right, level)
49+
return false
50+
end
51+
52+
-- Iterate over all keys in left and right, and compare their values recursively.
53+
visited[left] = true
54+
for k, v in next, left do
55+
if not deepcmp(v, rawget(right, k), next_level(level, k), visited, report_cb) then
56+
return false
57+
end
58+
end
59+
visited[left] = nil
60+
61+
visited[right] = true
62+
for k, v in next, right do
63+
if not deepcmp(rawget(left, k), v, next_level(level, k), visited, report_cb) then
64+
return false
65+
end
66+
end
67+
visited[right] = nil
68+
69+
return true
70+
end
71+
72+
function assertions.assert_same(left, right, message)
73+
local left_v, right_v, level
74+
local function report_cb(lv, rv, l)
75+
left_v, right_v, level = lv, rv, l
76+
end
77+
if not deepcmp(left, right, "", {}, report_cb) then
78+
if message ~= nil then
79+
message = string.format("assertion `left ~ right` failed: %s", tostring(message))
80+
else
81+
message = "assertion `left ~ right` failed!"
82+
end
83+
error(
84+
string.format("%s\n left%s: %s\n right%s: %s", message, level, tostring(left_v), level, tostring(right_v))
85+
)
86+
end
87+
end
88+
89+
return assertions

src/assertions.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use mlua::{Lua, Result, Table};
2+
3+
/// A loader for the `assertions` module.
4+
pub fn loader(lua: &Lua, name: String) -> Result<Table> {
5+
lua.load(include_str!("../lua/assertions.lua"))
6+
.set_name(format!("={name}"))
7+
.call(())
8+
}
9+
10+
/// Registers the `assertions` module in the given Lua state.
11+
pub fn register(lua: &Lua, name: Option<&str>) -> Result<()> {
12+
let name = name.unwrap_or("@assertions");
13+
lua.register_module(name, loader(lua, name.to_string())?)
14+
}

src/lib.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1 @@
1-
pub fn add(left: usize, right: usize) -> usize {
2-
left + right
3-
}
4-
5-
#[cfg(test)]
6-
mod tests {
7-
use super::*;
8-
9-
#[test]
10-
fn it_works() {
11-
let result = add(2, 2);
12-
assert_eq!(result, 4);
13-
}
14-
}
1+
pub mod assertions;

stylua.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
column_width = 120
2+
indent_type = "Spaces"
3+
indent_width = 4
4+
5+
[sort_requires]
6+
enabled = true

tests/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![cfg(test)]
2+
3+
use std::path::Path;
4+
5+
use mlua::{Lua, Result};
6+
7+
fn make_lua() -> Result<Lua> {
8+
let lua = Lua::new();
9+
10+
// Preload all modules
11+
mlua_stdlib::assertions::register(&lua, None)?;
12+
13+
Ok(lua)
14+
}
15+
16+
fn run_test(modname: &str) -> Result<()> {
17+
let lua = make_lua()?;
18+
lua.load(Path::new(&format!("tests/lua/{modname}_tests.lua")))
19+
.exec()
20+
}
21+
22+
macro_rules! include_tests {
23+
($($name:ident, )*) => { $(
24+
#[test]
25+
fn $name() -> Result<()> {
26+
run_test(stringify!($name))
27+
}
28+
)*}
29+
}
30+
31+
include_tests! {
32+
assertions,
33+
}

tests/lua/assertions_tests.lua

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
local assertions = require("@assertions")
2+
3+
local assert_eq = assertions.assert_eq
4+
local assert_ne = assertions.assert_ne
5+
local assert_same = assertions.assert_same
6+
7+
--
8+
-- Test assert_eq
9+
--
10+
assert_eq(1, 1)
11+
assert_eq(nil, nil)
12+
assert_eq(true, true)
13+
assert_eq("foo", "foo")
14+
15+
local ok, err = pcall(assert_eq, 1, 2)
16+
assert(not ok)
17+
assert(err:match("assertion `left == right` failed!"))
18+
assert(err:match(" left: 1"))
19+
assert(err:match(" right: 2"))
20+
21+
ok, err = pcall(assert_eq, 1, nil, "custom message")
22+
assert(not ok)
23+
assert(err:match("assertion `left == right` failed: custom message"))
24+
assert(err:match(" left: 1"))
25+
assert(err:match(" right: nil"))
26+
27+
ok, err = pcall(assert_eq, "foo", "bar")
28+
assert(not ok)
29+
assert(err:match(" left: foo"))
30+
assert(err:match(" right: bar"))
31+
32+
ok, err = pcall(assert_eq, {}, {})
33+
assert(not ok)
34+
assert(err:match(" left: table:"))
35+
assert(err:match(" right: table:"))
36+
37+
--
38+
-- Test assert_ne
39+
--
40+
assert_ne(1, 2)
41+
assert_ne(nil, 1)
42+
assert_ne(true, false)
43+
assert_ne("foo", "bar")
44+
45+
ok, err = pcall(assert_ne, 1, 1)
46+
assert(not ok)
47+
assert(err:match("assertion `left ~= right` failed!"))
48+
assert(err:match(" left: 1"))
49+
assert(err:match(" right: 1"))
50+
51+
ok, err = pcall(assert_ne, nil, nil, "custom message")
52+
assert(not ok)
53+
assert(err:match("assertion `left ~= right` failed: custom message"))
54+
assert(err:match(" left: nil"))
55+
assert(err:match(" right: nil"))
56+
57+
--
58+
-- Test assert_same
59+
--
60+
assert_same(1, 1)
61+
assert_same(nil, nil)
62+
assert_same(true, true)
63+
assert_same("foo", "foo")
64+
assert_same({ 1, 2, 3 }, { 1, 2, 3 })
65+
assert_same({ foo = "bar" }, { foo = "bar" })
66+
67+
ok, err = pcall(assert_same, 1, 2)
68+
assert(not ok)
69+
assert(err:match("assertion `left ~ right` failed!"))
70+
assert(err:match(" left: 1"))
71+
assert(err:match(" right: 2"))
72+
73+
ok, err = pcall(assert_same, 1, nil, "custom message")
74+
assert(not ok)
75+
assert(err:match("assertion `left ~ right` failed: custom message"))
76+
assert(err:match(" left: 1"))
77+
assert(err:match(" right: nil"))
78+
79+
ok, err = pcall(assert_same, "foo", "bar")
80+
assert(not ok)
81+
assert(err:match(" left: foo"))
82+
assert(err:match(" right: bar"))
83+
84+
ok, err = pcall(assert_same, { 1, 2, 3 }, { 1, 2, 4 })
85+
assert(not ok)
86+
assert(err:match(" left%[3%]: 3"))
87+
assert(err:match(" right%[3%]: 4"))
88+
89+
ok, err = pcall(assert_same, { foo = "bar" }, { foo = "baz" })
90+
assert(not ok)
91+
assert(err:match(' left%["foo"%]: bar'))
92+
assert(err:match(' right%["foo"%]: baz'))
93+
94+
-- Deep nested tables
95+
ok, err = pcall(assert_same, { foo = { bar = { baz = 1 } } }, { foo = { bar = { baz = function() end } } })
96+
assert(not ok)
97+
assert(err:match(' left%["foo"%]%["bar"%]%["baz"%]: 1'))
98+
assert(err:match(' right%["foo"%]%["bar"%]%["baz"%]: function:'))
99+
100+
-- Recursion
101+
local t = {}
102+
t[1] = t
103+
ok, err = pcall(assert_same, t, { {} })
104+
assert(not ok)
105+
assert(err:match(" left%[1%]: table:"))
106+
assert(err:match(" right%[1%]: table:"))

0 commit comments

Comments
 (0)