#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::approx_constant)]
#![cfg_attr(miri, allow(unused))]
pub(crate) mod prelude {
pub(crate) use crate as rune;
pub(crate) use crate::alloc;
pub(crate) use crate::alloc::fmt::TryWrite;
pub(crate) use crate::alloc::prelude::*;
pub(crate) use crate::ast;
pub(crate) use crate::compile::{self, ErrorKind, Located, Named};
pub(crate) use crate::diagnostics::{self, WarningDiagnosticKind};
pub(crate) use crate::hash;
pub(crate) use crate::macros;
pub(crate) use crate::module::InstallWith;
pub(crate) use crate::parse;
pub(crate) use crate::runtime::{
self, Bytes, DynamicTuple, Formatter, Function, InstAddress, MaybeTypeOf, Object, Output,
OwnedTuple, Protocol, RawAnyGuard, Ref, Stack, Tuple, TypeHash, TypeInfo, TypeOf,
UnsafeToRef, VecTuple, VmErrorKind, VmResult,
};
pub(crate) use crate::support::Result;
pub(crate) use crate::tests::{eval, run};
pub(crate) use crate::{
from_value, prepare, Any, Context, ContextError, Diagnostics, FromValue, Hash, Item,
ItemBuf, Module, Options, Source, Sources, Value, Vm,
};
pub(crate) use futures_executor::block_on;
pub(crate) use ::rust_alloc::string::{String, ToString};
pub(crate) use ::rust_alloc::sync::Arc;
pub(crate) use ::rust_alloc::vec::Vec;
pub(crate) use anyhow::Context as AnyhowContext;
}
use core::fmt;
use ::rust_alloc::string::String;
use ::rust_alloc::sync::Arc;
use anyhow::{Context as _, Error, Result};
use crate::runtime::{Args, VmError};
use crate::{
alloc, termcolor, BuildError, Context, Diagnostics, FromValue, Hash, Options, Source, Sources,
Unit, Vm,
};
#[derive(Debug)]
pub enum TestError {
Error(Error),
VmError(VmError),
}
impl From<Error> for TestError {
fn from(error: Error) -> Self {
TestError::Error(error)
}
}
impl From<alloc::Error> for TestError {
fn from(error: alloc::Error) -> Self {
TestError::Error(Error::new(error))
}
}
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TestError::Error(error) => write!(f, "Build error: {error}"),
TestError::VmError(error) => write!(f, "Vm error: {error}"),
}
}
}
impl core::error::Error for TestError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
TestError::Error(error) => Some(error.as_ref()),
TestError::VmError(error) => Some(error),
}
}
}
#[doc(hidden)]
pub fn compile_helper(source: &str, diagnostics: &mut Diagnostics) -> Result<Unit, BuildError> {
let context = crate::Context::with_default_modules().expect("setting up default modules");
let mut sources = Sources::new();
sources.insert(Source::new("main", source)?)?;
let mut options = Options::default();
options.script(true);
let unit = crate::prepare(&mut sources)
.with_context(&context)
.with_diagnostics(diagnostics)
.with_options(&options)
.build()?;
Ok(unit)
}
#[doc(hidden)]
pub fn vm(
context: &Context,
sources: &mut Sources,
diagnostics: &mut Diagnostics,
script: bool,
) -> Result<Vm, TestError> {
let mut options = Options::default();
if script {
options.script(true);
}
let result = crate::prepare(sources)
.with_context(context)
.with_diagnostics(diagnostics)
.with_options(&options)
.build();
let Ok(unit) = result else {
let mut buffer = termcolor::Buffer::no_color();
diagnostics
.emit(&mut buffer, sources)
.context("Emit diagnostics")?;
let error = Error::msg(String::from_utf8(buffer.into_inner()).context("Non utf-8 output")?);
return Err(TestError::Error(error));
};
let context = Arc::new(context.runtime()?);
Ok(Vm::new(context, Arc::new(unit)))
}
#[doc(hidden)]
pub fn run_helper<T>(
context: &Context,
sources: &mut Sources,
diagnostics: &mut Diagnostics,
args: impl Args,
script: bool,
) -> Result<T, TestError>
where
T: FromValue,
{
let mut vm = vm(context, sources, diagnostics, script)?;
let mut execute = if script {
vm.execute(Hash::EMPTY, args).map_err(TestError::VmError)?
} else {
vm.execute(["main"], args).map_err(TestError::VmError)?
};
let output = ::futures_executor::block_on(execute.async_complete())
.into_result()
.map_err(TestError::VmError)?;
crate::from_value(output).map_err(|error| TestError::VmError(error.into()))
}
#[doc(hidden)]
pub fn sources(source: &str) -> Sources {
let mut sources = Sources::new();
sources
.insert(Source::new("main", source).expect("Failed to build source"))
.expect("Failed to insert source");
sources
}
pub fn run<T>(context: &Context, source: &str, args: impl Args, script: bool) -> Result<T>
where
T: FromValue,
{
let mut sources = Sources::new();
sources.insert(Source::memory(source)?)?;
let mut diagnostics = Default::default();
let e = match run_helper(context, &mut sources, &mut diagnostics, args, script) {
Ok(value) => return Ok(value),
Err(e) => e,
};
match e {
TestError::Error(error) => Err(error),
TestError::VmError(e) => {
let mut buffer = termcolor::Buffer::no_color();
e.emit(&mut buffer, &sources).context("Emit diagnostics")?;
let buffer =
String::from_utf8(buffer.into_inner()).context("Decode output as utf-8")?;
Err(Error::msg(buffer))
}
}
}
macro_rules! expected {
($name:literal, $expected:pat, $actual:expr) => {
panic!(
"Did not match expected {}\nExpected: {}\n Actual: {:?}",
$name,
stringify!($expected),
$actual,
)
};
($name:literal, $expected:pat, $actual:expr, $extra:expr) => {
panic!(
"Did not match expected {}\nExpected: {}\n Actual: {:?}\n{}",
$name,
stringify!($expected),
$actual,
$extra,
)
};
}
#[track_caller]
pub(crate) fn eval<T>(source: impl AsRef<str>) -> T
where
T: FromValue,
{
let source = source.as_ref();
let context = Context::with_default_modules().expect("Failed to build context");
match run(&context, source, (), true) {
Ok(output) => output,
Err(error) => {
panic!("Program failed to run:\n{error}\n{source}");
}
}
}
macro_rules! rune {
($($tt:tt)*) => {
$crate::tests::eval(stringify!($($tt)*))
};
}
macro_rules! rune_assert {
($($tt:tt)*) => {{
let value: bool = $crate::tests::eval(stringify!($($tt)*));
assert!(value, "Rune program is not `true`:\n{}", stringify!($($tt)*));
}};
}
macro_rules! rune_n {
($(mod $module:expr,)* $args:expr, $($tt:tt)*) => {{
let mut context = $crate::Context::with_default_modules().expect("Failed to build context");
$(context.install(&$module).expect("Failed to install native module");)*
$crate::tests::run(&context, stringify!($($tt)*), $args, false).expect("Program ran unsuccessfully")
}};
}
macro_rules! assert_vm_error {
($source:expr, $pat:pat => $cond:block) => {
assert_vm_error!(() => $source, $pat => $cond)
};
($ty:ty => $source:expr, $pat:pat => $cond:block) => {{
let context = $crate::Context::with_default_modules().unwrap();
let mut diagnostics = Default::default();
let mut sources = $crate::tests::sources($source);
let e = match $crate::tests::run_helper::<$ty>(&context, &mut sources, &mut diagnostics, (), true) {
Err(e) => e,
actual => {
expected!("program error", Err(e), actual, $source)
}
};
let e = match e {
$crate::tests::TestError::VmError(e) => e,
actual => {
expected!("vm error", VmError(e), actual, $source)
}
};
match e.into_kind() {
$pat => $cond,
actual => {
expected!("error", $pat, actual, $source)
}
}
}};
}
macro_rules! assert_parse {
($source:expr) => {{
let mut diagnostics = Default::default();
$crate::tests::compile_helper($source, &mut diagnostics).unwrap()
}};
}
macro_rules! assert_errors {
($source:expr, $span:pat $(, $pat:pat $(=> $cond:expr)?)+ $(,)?) => {{
let mut diagnostics = Default::default();
let _ = $crate::tests::compile_helper($source, &mut diagnostics).unwrap_err();
let mut it = diagnostics.into_diagnostics().into_iter();
$(
let e = match it.next().expect("expected error") {
rune::diagnostics::Diagnostic::Fatal(e) => e,
actual => {
expected!("fatal diagnostic", Fatal(e), actual)
}
};
let e = match e.into_kind() {
rune::diagnostics::FatalDiagnosticKind::CompileError(e) => (e),
actual => {
expected!("compile error", CompileError(e), actual)
}
};
let span = rune::ast::Spanned::span(&e);
#[allow(irrefutable_let_patterns)]
let $span = span else {
expected!("span", $span, span)
};
match e.into_kind() {
$pat => {$($cond)*},
#[allow(unreachable_patterns)]
actual => {
expected!("error", $pat, actual)
}
}
)+
}};
}
macro_rules! assert_warnings {
($source:expr, $span:pat $(, $pat:pat $(=> $cond:expr)?)* $(,)?) => {{
let mut diagnostics = Default::default();
let _ = $crate::tests::compile_helper($source, &mut diagnostics).expect("source should compile");
assert!(diagnostics.has_warning(), "no warnings produced");
let mut it = diagnostics.into_diagnostics().into_iter();
$(
let warning = it.next().expect("expected a warning");
let warning = match warning {
rune::diagnostics::Diagnostic::Warning(warning) => warning,
actual => {
expected!("warning diagnostic", $pat, actual)
}
};
let span = rune::ast::Spanned::span(&warning);
#[allow(irrefutable_let_patterns)]
let $span = span else {
expected!("span", $span, span)
};
match warning.into_kind() {
$pat => {$($cond)*},
actual => {
expected!("warning", $pat, actual)
}
}
)*
assert!(it.next().is_none(), "there should be no more warnings");
}};
}
macro_rules! assert_matches {
($value:expr, $pat:pat) => {
match $value {
$pat => (),
other => panic!("expected {}, but was {:?}", stringify!($pat), other),
}
};
}
macro_rules! prelude {
() => {
#[allow(unused_imports)]
use crate::tests::prelude::*;
};
}
#[cfg(not(miri))]
mod attribute;
#[cfg(not(miri))]
mod binary;
#[cfg(not(miri))]
mod bug_326;
#[cfg(not(miri))]
mod bug_344;
#[cfg(not(miri))]
mod bug_417;
#[cfg(not(miri))]
mod bug_422;
#[cfg(not(miri))]
mod bug_428;
#[cfg(not(miri))]
mod bug_454;
#[cfg(not(miri))]
mod bug_700;
#[cfg(not(miri))]
mod bug_905;
#[cfg(not(miri))]
mod bugfixes;
#[cfg(not(miri))]
mod builtin_macros;
#[cfg(not(miri))]
mod capture;
#[cfg(not(miri))]
mod comments;
#[cfg(not(miri))]
mod compiler_docs;
#[cfg(not(miri))]
mod compiler_expr_assign;
#[cfg(not(miri))]
mod compiler_fn;
#[cfg(not(miri))]
mod compiler_general;
#[cfg(not(miri))]
mod compiler_literals;
#[cfg(not(miri))]
mod compiler_paths;
#[cfg(not(miri))]
mod compiler_patterns;
#[cfg(not(miri))]
mod compiler_use;
#[cfg(not(miri))]
mod compiler_visibility;
#[cfg(not(miri))]
mod compiler_warnings;
#[cfg(not(miri))]
mod continue_;
#[cfg(not(miri))]
mod core_macros;
#[cfg(not(miri))]
mod custom_macros;
#[cfg(not(miri))]
mod debug_fmt;
#[cfg(not(miri))]
mod deprecation;
#[cfg(not(miri))]
mod destructuring;
#[cfg(not(miri))]
mod esoteric_impls;
#[cfg(not(miri))]
mod external_constructor;
#[cfg(not(miri))]
mod external_generic;
#[cfg(not(miri))]
mod external_match;
#[cfg(not(miri))]
mod external_ops;
mod function_guardedargs;
#[cfg(not(miri))]
mod getter_setter;
#[cfg(not(miri))]
mod iterator;
#[cfg(not(miri))]
mod macros;
#[cfg(not(miri))]
mod moved;
#[cfg(not(miri))]
mod option;
#[cfg(not(miri))]
mod patterns;
#[cfg(not(miri))]
mod quote;
#[cfg(not(miri))]
mod range;
#[cfg(not(miri))]
mod reference_error;
#[cfg(not(miri))]
mod rename_type;
#[cfg(not(miri))]
mod result;
#[cfg(not(miri))]
mod static_typing;
#[cfg(not(miri))]
mod tuple;
#[cfg(not(miri))]
mod type_name_native;
#[cfg(not(miri))]
mod unit_constants;
#[cfg(not(miri))]
mod unreachable;
#[cfg(not(miri))]
mod vm_arithmetic;
#[cfg(not(miri))]
mod vm_assign_exprs;
#[cfg(not(miri))]
mod vm_async_block;
#[cfg(not(miri))]
mod vm_blocks;
#[cfg(not(miri))]
mod vm_closures;
#[cfg(not(miri))]
mod vm_const_exprs;
#[cfg(not(miri))]
mod vm_early_termination;
#[cfg(not(miri))]
mod vm_function;
#[cfg(not(miri))]
mod vm_function_pointers;
#[cfg(not(miri))]
mod vm_general;
#[cfg(not(miri))]
mod vm_literals;
#[cfg(not(miri))]
mod vm_not_used;
#[cfg(not(miri))]
mod vm_result;
#[cfg(not(miri))]
mod vm_test_from_value_derive;
#[cfg(not(miri))]
mod vm_test_imports;
#[cfg(not(miri))]
mod vm_test_instance_fns;
#[cfg(not(miri))]
mod vm_test_linked_list;
#[cfg(not(miri))]
mod vm_test_mod;
#[cfg(not(miri))]
mod vm_try;
#[cfg(not(miri))]
mod wildcard_imports;