Add Function::coverage
for Luau to obtain coverage report
This commit is contained in:
parent
bcf2cbea37
commit
0076aa735a
|
@ -1,6 +1,7 @@
|
|||
use std::mem;
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::ffi;
|
||||
|
@ -29,6 +30,17 @@ pub struct FunctionInfo {
|
|||
pub last_line_defined: i32,
|
||||
}
|
||||
|
||||
/// Luau function coverage snapshot.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CoverageInfo {
|
||||
pub function: Option<std::string::String>,
|
||||
pub line_defined: i32,
|
||||
pub depth: i32,
|
||||
pub hits: Vec<i32>,
|
||||
}
|
||||
|
||||
impl<'lua> Function<'lua> {
|
||||
/// Calls the function, passing `args` as function arguments.
|
||||
///
|
||||
|
@ -274,9 +286,6 @@ impl<'lua> Function<'lua> {
|
|||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
use std::os::raw::c_void;
|
||||
use std::slice;
|
||||
|
||||
unsafe extern "C" fn writer(
|
||||
_state: *mut ffi::lua_State,
|
||||
buf: *const c_void,
|
||||
|
@ -304,6 +313,58 @@ impl<'lua> Function<'lua> {
|
|||
|
||||
data
|
||||
}
|
||||
|
||||
/// Retrieves recorded coverage information about this Lua function including inner calls.
|
||||
///
|
||||
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
|
||||
/// per each executed inner function.
|
||||
///
|
||||
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
|
||||
#[cfg(any(feature = "luau", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn coverage<F>(&self, mut func: F)
|
||||
where
|
||||
F: FnMut(CoverageInfo),
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
|
||||
data: *mut c_void,
|
||||
function: *const c_char,
|
||||
line_defined: c_int,
|
||||
depth: c_int,
|
||||
hits: *const c_int,
|
||||
size: usize,
|
||||
) {
|
||||
let function = if !function.is_null() {
|
||||
Some(CStr::from_ptr(function).to_string_lossy().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rust_callback = &mut *(data as *mut F);
|
||||
rust_callback(CoverageInfo {
|
||||
function,
|
||||
line_defined,
|
||||
depth,
|
||||
hits: slice::from_raw_parts(hits, size).to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
assert_stack(lua.state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
let func_ptr = &mut func as *mut F as *mut c_void;
|
||||
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> PartialEq for Function<'lua> {
|
||||
|
|
|
@ -128,7 +128,7 @@ pub use crate::hook::HookTriggers;
|
|||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{chunk::Compiler, types::VmState};
|
||||
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
|
|
@ -21,7 +21,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
|||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::VmState as LuaVmState;
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::fs;
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
|
@ -212,3 +212,76 @@ fn test_interrupts() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coverate() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_compiler(Compiler::default().set_coverage_level(1));
|
||||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"local v = vector(1, 2, 3)
|
||||
assert(v.x == 1 and v.y == 2 and v.z == 3)
|
||||
|
||||
function abc(i)
|
||||
if i < 5 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
(function()
|
||||
(function() abc(10) end)()
|
||||
end)()
|
||||
"#,
|
||||
)
|
||||
.into_function()?;
|
||||
|
||||
f.call(())?;
|
||||
|
||||
let mut report = Vec::new();
|
||||
f.coverage(|cov| {
|
||||
report.push(cov);
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
report[0],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 1,
|
||||
depth: 0,
|
||||
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[1],
|
||||
CoverageInfo {
|
||||
function: Some("abc".into()),
|
||||
line_defined: 4,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[2],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 12,
|
||||
depth: 1,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
report[3],
|
||||
CoverageInfo {
|
||||
function: None,
|
||||
line_defined: 13,
|
||||
depth: 2,
|
||||
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue