Skip to content

Commit

Permalink
feat(stdlib): Test framework improvements (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
tibordp authored Oct 5, 2024
1 parent 2288cde commit 8a621ba
Show file tree
Hide file tree
Showing 18 changed files with 343 additions and 208 deletions.
3 changes: 1 addition & 2 deletions examples/unit_tests.alu
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ mod tests {
}

#[test]
#[test::should_panic]
fn test_panic() {
my_panic();
test::assert_panics!(my_panic());
}
}
5 changes: 1 addition & 4 deletions libraries/json/marshal.alu
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,7 @@ mod tests {
}

impl Test {
fn equals(self: &Test, other: &Test) -> bool {
self.a == other.a && self.b == other.b
}
mixin std::cmp::Equatable<Test>;
mixin std::cmp::DefaultEquatable<Test>;
}

#[test]
Expand Down
60 changes: 50 additions & 10 deletions src/alumina-boot/src/ir/mono/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub enum Intr {
HasAttribute,
ValueOf,
Expect,
WithSpanOf,
}

pub fn intrinsic_kind(name: &str) -> Option<Intr> {
Expand Down Expand Up @@ -99,6 +100,7 @@ pub fn intrinsic_kind(name: &str) -> Option<Intr> {
"has_attribute" => HasAttribute,
"value_of" => ValueOf,
"expect" => Expect,
"with_span_of" => WithSpanOf,
_ => return None,
};

Expand All @@ -113,12 +115,36 @@ impl<'a, 'ast, 'ir> super::Mono<'a, 'ast, 'ir> {
callee: &ast::Intrinsic,
generic_args: &[ast::TyP<'ast>],
args: &[ast::ExprP<'ast>],
type_hint: Option<ir::TyP<'ir>>,
) -> Result<ir::ExprP<'ir>, AluminaError> {
use Intr::*;

let generic_args = generic_args
.iter()
.map(|e| self.lower_type_unrestricted(e))
.collect::<Result<Vec<_>, _>>()?;

macro_rules! generic {
($n:literal) => {
match generic_args.get($n) {
Some(arg) => arg,
None => ice!(self.diag, "not enough generic arguments to intrinsic"),
}
};
}

match callee.kind {
// This one is special since it lowers the expression itself
WithSpanOf => {
let inner_expr = match args.first() {
Some(expr) => expr,
_ => ice!(self.diag, "not enough arguments to intrinsic"),
};
return self.intr_with_span_of(generic!(0), inner_expr, type_hint);
}
_ => {}
}

let args = args
.iter()
.map(|e| self.lower_expr(e, None))
Expand All @@ -132,16 +158,6 @@ impl<'a, 'ast, 'ir> super::Mono<'a, 'ast, 'ir> {
}
};
}

macro_rules! generic {
($n:literal) => {
match generic_args.get($n) {
Some(arg) => arg,
None => ice!(self.diag, "not enough generic arguments to intrinsic"),
}
};
}
use Intr::*;
match callee.kind {
MakeVtable => {
if let ir::Ty::Tuple(inner) = generic!(0) {
Expand Down Expand Up @@ -191,6 +207,7 @@ impl<'a, 'ast, 'ir> super::Mono<'a, 'ast, 'ir> {
HasAttribute => self.intr_has_attribute(generic!(0), arg!(0), span),
ValueOf => self.intr_value_of(generic!(0), span),
Expect => self.intr_expect(arg!(0), arg!(1), span),
WithSpanOf => unreachable!(),
}
}

Expand Down Expand Up @@ -1050,4 +1067,27 @@ impl<'a, 'ast, 'ir> super::Mono<'a, 'ast, 'ir> {
span,
))
}

fn intr_with_span_of(
&mut self,
ty: ir::TyP<'ir>,
inner: ast::ExprP<'ast>,
type_hint: Option<ir::TyP<'ir>>,
) -> Result<ir::ExprP<'ir>, AluminaError> {
let span = match ty {
ir::Ty::Item(item) => match item.get().with_backtrace(&self.diag)? {
ir::Item::Function(f) => f.span,
ir::Item::Static(s) => s.span,
ir::Item::Const(c) => c.span,
ir::Item::Protocol(p) => p.span,
ir::Item::StructLike(s) => s.span,
ir::Item::Enum(e) => e.span,
_ => None,
},
_ => None,
};

let _guard = self.diag.push_span(span);
self.lower_expr(inner, type_hint)
}
}
1 change: 1 addition & 0 deletions src/alumina-boot/src/ir/mono/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5004,6 +5004,7 @@ impl<'a, 'ast, 'ir> Mono<'a, 'ast, 'ir> {
intrinsic,
generic_args.unwrap_or(&[]),
args,
type_hint,
);
}

Expand Down
7 changes: 7 additions & 0 deletions sysroot/std/intrinsics.alu
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@ extern "intrinsic" fn has_attribute<T>(name: &[u8]) -> bool;
/// Use [std::likely] and [std::unlikely] instead.
extern "intrinsic" fn expect(value: bool, to_be: bool) -> bool;

/// Lowers the inner expression using the definition span of the provided generic argument.
///
/// This intrinsic may return a lvalue.
///
/// No stable equivalent.
extern "intrinsic" fn with_span_of<T, E>(expr: E) -> E;

#[cfg(boot)]
{
// These are only useful in alumina-boot, aluminac will use LLVM intrinsics that are
Expand Down
37 changes: 23 additions & 14 deletions sysroot/std/io/mod.alu
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@
type Result<T> = std::result::Result<T, Error>;


/// Buffer for formatted standard I/O.
/// Minimum size of the buffer used for convenience I/O macros.
///
/// A single thread local buffer is used for convenience stdio formatting macros. This
/// buffer has limited a purpose (if you need a larger buffer, use [io::StdioStream] directly).
/// As many formatters write individual characters, doing a syscall for every one
/// would be a performance killer.
#[thread_local] static FMT_BUF: [u8; 256];
/// Convenience I/O macros such as [print] and [println] use a small buffer to format
/// the output. This buffer is allocated on the stack and is mostly there to prevent
/// formatters that write single characters from issuing a lot of syscalls.
///
/// The actual buffer size is the maximum of this constant and the length of the format
/// string plus this constant.
///
/// There is no buffering between separate invocations of the macros, i.e. [print] will
/// flush the buffer after each invocation.
///
/// If you require larger buffers, consider using the [BufferedWriter] type directly.
const FMT_BUF_SIZE = 32usize;

/// Prints a formatted string to standard output.
///
Expand All @@ -32,8 +39,11 @@ macro print($fmt_str, $arg...) {
let buf = fmt::format!($fmt_str, $arg$...).unwrap();
intrinsics::const_note(buf[..]);
buf.free();
} else when macros::count!($arg$...) == 0 {
StdioStream::stdout().write_all($fmt_str).unwrap();
} else {
let fmt = BufferedWriter::from_slice(&StdioStream::stdout(), &FMT_BUF);
let buf: [u8; cmp::max(FMT_BUF_SIZE, $fmt_str.len() + FMT_BUF_SIZE)];
let fmt = BufferedWriter::from_slice(&StdioStream::stdout(), &buf);
fmt::write!(&fmt, $fmt_str, $arg$...).unwrap();
fmt.flush().unwrap();
}
Expand All @@ -49,9 +59,7 @@ macro println($fmt_str, $arg...) {
if runtime::in_const_context() {
print!($fmt_str, $arg$...);
} else {
let fmt = BufferedWriter::from_slice(&StdioStream::stdout(), &FMT_BUF);
fmt::writeln!(&fmt, $fmt_str, $arg$...).unwrap();
fmt.flush().unwrap();
print!(concat!($fmt_str, "\n"), $arg$...);
}
}

Expand All @@ -66,8 +74,11 @@ macro eprint($fmt_str, $arg...) {
let buf = fmt::format!($fmt_str, $arg$...).unwrap();
intrinsics::const_warning(buf[..]);
buf.free();
} else when macros::count!($arg$...) == 0 {
StdioStream::stderr().write_all($fmt_str).unwrap();
} else {
let fmt = BufferedWriter::from_slice(&StdioStream::stderr(), &FMT_BUF);
let buf: [u8; cmp::max(FMT_BUF_SIZE, $fmt_str.len() + FMT_BUF_SIZE)];
let fmt = BufferedWriter::from_slice(&StdioStream::stderr(), &buf);
fmt::write!(&fmt, $fmt_str, $arg$...).unwrap();
fmt.flush().unwrap();
}
Expand All @@ -83,9 +94,7 @@ macro eprintln($fmt_str, $arg...) {
if runtime::in_const_context() {
eprint!($fmt_str, $arg$...);
} else {
let fmt = BufferedWriter::from_slice(&StdioStream::stderr(), &FMT_BUF);
fmt::writeln!(&fmt, $fmt_str, $arg$...).unwrap();
fmt.flush().unwrap();
eprint!(concat!($fmt_str, "\n"), $arg$...);
}
}

Expand Down
3 changes: 1 addition & 2 deletions sysroot/std/iter.alu
Original file line number Diff line number Diff line change
Expand Up @@ -2647,9 +2647,8 @@ mod tests {
}

#[test]
#[test::should_panic]
fn test_chunks_0() {
(0..10).chunks(0);
test::assert_panics!((0..10).chunks(0));
}

#[test]
Expand Down
18 changes: 6 additions & 12 deletions sysroot/std/mem.alu
Original file line number Diff line number Diff line change
Expand Up @@ -1281,45 +1281,39 @@ mod tests {
#[cfg(debug)]

{ #[test]
#[test::should_panic]
fn test_bounds_check_1() {
let a = [1,2,3,4,5].as_slice();
a[5];
test::assert_panics!(a[5]);
}

#[test]
#[test::should_panic]
fn test_bounds_check_2() {
let a = [1,2,3,4,5].as_slice();
a[4..3];
test::assert_panics!(a[4..3]);
}

#[test]
#[test::should_panic]
fn test_bounds_check_3() {
let a = [1,2,3,4,5].as_slice();
a[6..];
test::assert_panics!(a[6..]);
}

#[test]
#[test::should_panic]
fn test_bounds_check_4() {
let a = [1,2,3,4,5].as_slice();
a[..6];
test::assert_panics!(a[..6]);
}

#[test]
#[test::should_panic]
fn test_bounds_check_5() {
let a = [1,2,3,4,5].as_slice();
a[..=5];
test::assert_panics!(a[..=5]);
}

#[test]
#[test::should_panic]
fn test_bounds_check_6() {
let a = [1,2,3,4,5].as_slice();
a[0..=5];
test::assert_panics!(a[0..=5]);
}
}

Expand Down
3 changes: 1 addition & 2 deletions sysroot/std/option.alu
Original file line number Diff line number Diff line change
Expand Up @@ -657,11 +657,10 @@ mod tests {
}

#[test]
#[test::should_panic]
fn option_unwrap_none() {
let opt2 = Option::none::<i32>();

opt2.unwrap();
test::assert_panics!(opt2.unwrap());
}

#[test]
Expand Down
3 changes: 3 additions & 0 deletions sysroot/std/panicking.alu
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ struct PanicInfo {
/// error handling, as Alumina panics do not unwind the stack, which means that any resources
/// that need to be cleaned up will not be cleaned up.
///
/// See also [test::assert_panics] for a more convenient way to test for panics.
///
/// ```dubious
/// use std::panicking::catch_panic;
/// use std::collections::Vector;
Expand Down Expand Up @@ -342,6 +344,7 @@ mod internal {
}

#[const(always)]
#[diag::hide_from_backtrace]
fn const_panic_impl(
bare: bool,
file: &[u8],
Expand Down
12 changes: 4 additions & 8 deletions sysroot/std/random/mod.alu
Original file line number Diff line number Diff line change
Expand Up @@ -1191,26 +1191,22 @@ mod tests {
}

#[test]
#[test::should_panic]
fn test_distr_weighted_index_empty() {
WeightedIndex::new::<i32>(&[]);
test::assert_panics!(WeightedIndex::new::<i32>(&[]));
}

#[test]
#[test::should_panic]
fn test_distr_weighted_index_zero_weight() {
WeightedIndex::new(&[0, 0, 0, 0, 0, 0]);
test::assert_panics!(WeightedIndex::new(&[0, 0, 0, 0, 0, 0]));
}

#[test]
#[test::should_panic]
fn test_distr_weighted_index_negative_weight() {
WeightedIndex::new(&[0, 1, 2, 3, 4, -5]);
test::assert_panics!(WeightedIndex::new(&[0, 1, 2, 3, 4, -5]));
}

#[test]
#[test::should_panic]
fn test_distr_weighted_index_negative_weight_1() {
WeightedIndex::new(&[-1, 1, 2, 3, 4]);
test::assert_panics!(WeightedIndex::new(&[-1, 1, 2, 3, 4]));
}
}
3 changes: 1 addition & 2 deletions sysroot/std/runtime/mod.alu
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,7 @@ mod tests {

#[cfg(all(coroutines, debug))]
#[test]
#[test::should_panic]
fn test_yield_outside_coroutine() {
internal::coroutine_yield(&(), &());
test::assert_panics!(internal::coroutine_yield(&(), &()));
}
}
2 changes: 2 additions & 0 deletions sysroot/std/typing.alu
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ impl Type<T> {
}
}



/// Returns the pretty name of the type.
fn debug_name(self: Type<T>) -> &[u8] {
type_name::<T>()
Expand Down
Loading

0 comments on commit 8a621ba

Please sign in to comment.