use crate::test_util::assert_tokens_eq;
use super::*;
use syn::{Block, Type, parse_quote};
fn make_fn_body() -> Block {
parse_quote! {
{
this_is_the_body()
}
}
}
fn make_return_type() -> Type {
parse_quote! { SomeType }
}
#[test]
fn simple_requires() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
let (__anodized_output): (#ret_type) = ((|| #body)());
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn requires_disable_runtime_checks() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
if false {
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
}
let (__anodized_output): (#ret_type) = ((|| #body)());
__anodized_output
}
};
let observed = Backend::NO_CHECK
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn requires_no_panic_runtime() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
if !((| | CONDITION_1)()) {
eprintln!("Precondition failed: {}", "CONDITION_1");
}
let (__anodized_output): (#ret_type) = ((|| #body)());
__anodized_output
}
};
let observed = Backend::CHECK_AND_PRINT
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_maintains() {
let spec: Spec = parse_quote! {
maintains: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Pre-invariant failed: {}", "CONDITION_1");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_1)(), "Post-invariant failed: {}", "CONDITION_1");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_ensures() {
let spec: Spec = parse_quote! {
ensures: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((|output: &#ret_type| CONDITION_1)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_1");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_requires_and_maintains() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
maintains: CONDITION_2,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
assert!((| | CONDITION_2)(), "Pre-invariant failed: {}", "CONDITION_2");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_2)(), "Post-invariant failed: {}", "CONDITION_2");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_requires_and_ensures() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
ensures: CONDITION_2,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((|output: &#ret_type| CONDITION_2)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_2");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_maintains_and_ensures() {
let spec: Spec = parse_quote! {
maintains: CONDITION_1,
ensures: CONDITION_2,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Pre-invariant failed: {}", "CONDITION_1");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_1)(), "Post-invariant failed: {}", "CONDITION_1");
assert!((|output: &#ret_type| CONDITION_2)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_2");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_requires_maintains_and_ensures() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
maintains: CONDITION_2,
ensures: CONDITION_3,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
assert!((| | CONDITION_2)(), "Pre-invariant failed: {}", "CONDITION_2");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_2)(), "Post-invariant failed: {}", "CONDITION_2");
assert!((|output: &#ret_type| CONDITION_3)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_3");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn simple_async_requires_maintains_and_ensures() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
maintains: CONDITION_2,
ensures: CONDITION_3,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = true;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
assert!((| | CONDITION_2)(), "Pre-invariant failed: {}", "CONDITION_2");
let (__anodized_output): (#ret_type) = ((async || #body)().await);
assert!((| | CONDITION_2)(), "Post-invariant failed: {}", "CONDITION_2");
assert!((|output: &#ret_type| CONDITION_3)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_3");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn multiple_conditions_in_clauses() {
let spec: Spec = parse_quote! {
requires: [CONDITION_1, CONDITION_2],
maintains: [CONDITION_3, CONDITION_4],
ensures: [CONDITION_5, CONDITION_6],
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
assert!((| | CONDITION_2)(), "Precondition failed: {}", "CONDITION_2");
assert!((| | CONDITION_3)(), "Pre-invariant failed: {}", "CONDITION_3");
assert!((| | CONDITION_4)(), "Pre-invariant failed: {}", "CONDITION_4");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_3)(), "Post-invariant failed: {}", "CONDITION_3");
assert!((| | CONDITION_4)(), "Post-invariant failed: {}", "CONDITION_4");
assert!((|output: &#ret_type| CONDITION_5)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_5");
assert!((|output: &#ret_type| CONDITION_6)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_6");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn binds_parameter() {
let spec: Spec = parse_quote! {
binds: OUTPUT_PATTERN,
ensures: CONDITION_1,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((|OUTPUT_PATTERN: &#ret_type| CONDITION_1)(&__anodized_output), "Postcondition failed: {}", "| OUTPUT_PATTERN | CONDITION_1");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn ensures_with_mixed_conditions() {
let spec: Spec = parse_quote! {
ensures: [
CONDITION_1,
|PATTERN_1| CONDITION_2,
CONDITION_3,
|PATTERN_2| CONDITION_4
],
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((|output: &#ret_type| CONDITION_1)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_1");
assert!((|PATTERN_1: &#ret_type| CONDITION_2)(&__anodized_output), "Postcondition failed: {}", "| PATTERN_1 | CONDITION_2");
assert!((|output: &#ret_type| CONDITION_3)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_3");
assert!((|PATTERN_2: &#ret_type| CONDITION_4)(&__anodized_output), "Postcondition failed: {}", "| PATTERN_2 | CONDITION_4");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn cfg_attributes() {
let spec: Spec = parse_quote! {
#[cfg(SETTING_1)]
requires: CONDITION_1,
#[cfg(SETTING_2)]
maintains: CONDITION_2,
#[cfg(SETTING_3)]
ensures: CONDITION_3,
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
if cfg!(SETTING_1) {
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
}
if cfg!(SETTING_2) {
assert!((| | CONDITION_2)(), "Pre-invariant failed: {}", "CONDITION_2");
}
let (__anodized_output): (#ret_type) = ((|| #body)());
if cfg!(SETTING_2) {
assert!((| | CONDITION_2)(), "Post-invariant failed: {}", "CONDITION_2");
}
if cfg!(SETTING_3) {
assert!((|output: &#ret_type| CONDITION_3)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_3");
}
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn cfg_on_single_and_list_conditions() {
let spec: Spec = parse_quote! {
#[cfg(SETTING_1)]
requires: CONDITION_1,
maintains: [CONDITION_2, CONDITION_3],
#[cfg(SETTING_2)]
ensures: [CONDITION_4, CONDITION_5],
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
if cfg!(SETTING_1) {
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
}
assert!((| | CONDITION_2)(), "Pre-invariant failed: {}", "CONDITION_2");
assert!((| | CONDITION_3)(), "Pre-invariant failed: {}", "CONDITION_3");
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_2)(), "Post-invariant failed: {}", "CONDITION_2");
assert!((| | CONDITION_3)(), "Post-invariant failed: {}", "CONDITION_3");
if cfg!(SETTING_2) {
assert!((|output: &#ret_type| CONDITION_4)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_4");
}
if cfg!(SETTING_2) {
assert!((|output: &#ret_type| CONDITION_5)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_5");
}
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn complex_mixed_conditions() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
#[cfg(SETTING_1)]
requires: [CONDITION_2, CONDITION_3],
maintains: [CONDITION_4, CONDITION_5],
#[cfg(SETTING_2)]
maintains: CONDITION_6,
ensures: CONDITION_7,
#[cfg(SETTING_3)]
ensures: [CONDITION_8, |PATTERN_1| CONDITION_9],
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
if cfg!(SETTING_1) {
assert!((| | CONDITION_2)(), "Precondition failed: {}", "CONDITION_2");
}
if cfg!(SETTING_1) {
assert!((| | CONDITION_3)(), "Precondition failed: {}", "CONDITION_3");
}
assert!((| | CONDITION_4)(), "Pre-invariant failed: {}", "CONDITION_4");
assert!((| | CONDITION_5)(), "Pre-invariant failed: {}", "CONDITION_5");
if cfg!(SETTING_2) {
assert!((| | CONDITION_6)(), "Pre-invariant failed: {}", "CONDITION_6");
}
let (__anodized_output): (#ret_type) = ((|| #body)());
assert!((| | CONDITION_4)(), "Post-invariant failed: {}", "CONDITION_4");
assert!((| | CONDITION_5)(), "Post-invariant failed: {}", "CONDITION_5");
if cfg!(SETTING_2) {
assert!((| | CONDITION_6)(), "Post-invariant failed: {}", "CONDITION_6");
}
assert!((|output: &#ret_type| CONDITION_7)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_7");
if cfg!(SETTING_3) {
assert!((|output: &#ret_type| CONDITION_8)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_8");
}
if cfg!(SETTING_3) {
assert!((|PATTERN_1: &#ret_type| CONDITION_9)(&__anodized_output), "Postcondition failed: {}", "| PATTERN_1 | CONDITION_9");
}
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}
#[test]
fn captures() {
let spec: Spec = parse_quote! {
requires: CONDITION_1,
captures: [
EXPR_1 as ALIAS_1,
EXPR_2 as ALIAS_2,
],
ensures: [
CONDITION_2,
CONDITION_3,
],
};
let body = make_fn_body();
let ret_type = make_return_type();
let is_async = false;
let expected: Block = parse_quote! {
{
assert!((| | CONDITION_1)(), "Precondition failed: {}", "CONDITION_1");
let (ALIAS_1, ALIAS_2, __anodized_output): (_, _, #ret_type) = ((| | EXPR_1) (), (| | EXPR_2) (), (|| #body)());
assert!((|output: &#ret_type| CONDITION_2)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_2");
assert!((|output: &#ret_type| CONDITION_3)(&__anodized_output), "Postcondition failed: {}", "| output | CONDITION_3");
__anodized_output
}
};
let observed = Backend::CHECK_AND_PANIC
.instrument_fn_body(&spec, &body, is_async, &ret_type)
.unwrap();
assert_tokens_eq(&observed, &expected);
}