use crate::test_helpers::*;
use slicec::diagnostics::{Diagnostic, Error};
use slicec::grammar::*;
use test_case::test_case;
#[test]
fn can_have_no_parameters() {
let slice = "
module Test
interface I {
op()
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
assert!(operation.parameters().is_empty());
}
#[test]
fn can_have_no_return_type() {
let slice = "
module Test
interface I {
op(a: int32)
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
assert!(operation.return_members().is_empty());
}
#[test]
fn can_contain_tags() {
let slice = "
module Test
interface I {
op(tag(1) a: int32?)
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let tag_def = operation.parameters()[0].tag();
assert_eq!(tag_def, Some(1));
}
#[test]
fn parameter_and_return_can_have_the_same_tag() {
let slice = "
module Test
interface I {
op(tag(1) a: int32?) -> tag(1) string?
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let parameter_tag = operation.parameters()[0].tag();
let return_tag = operation.return_members()[0].tag();
assert_eq!(parameter_tag, Some(1));
assert_eq!(return_tag, Some(1));
}
#[test]
fn can_have_parameters() {
let slice = "
module Test
interface I {
op(a: int32, b: string, c: varuint62)
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let parameters = operation.parameters();
assert_eq!(parameters.len(), 3);
assert_eq!(parameters[0].identifier(), "a");
assert_eq!(parameters[1].identifier(), "b");
assert_eq!(parameters[2].identifier(), "c");
assert!(matches!(
parameters[0].data_type.concrete_type(),
Types::Primitive(Primitive::Int32),
));
assert!(matches!(
parameters[1].data_type.concrete_type(),
Types::Primitive(Primitive::String),
));
assert!(matches!(
parameters[2].data_type.concrete_type(),
Types::Primitive(Primitive::VarUInt62),
));
}
#[test]
fn can_have_return_value() {
let slice = "
module Test
interface I {
op() -> Result<bool, string>
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let returns = operation.return_members();
assert_eq!(returns.len(), 1);
assert_eq!(returns[0].identifier(), "returnValue");
assert!(matches!(returns[0].data_type.concrete_type(), Types::ResultType(_)));
}
#[test]
fn can_have_return_tuple() {
let slice = "
module Test
interface I {
op() -> (r1: string, r2: bool)
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let returns = operation.return_members();
assert_eq!(returns.len(), 2);
assert_eq!(returns[0].identifier(), "r1");
assert_eq!(returns[1].identifier(), "r2");
assert!(matches!(
returns[0].data_type.concrete_type(),
Types::Primitive(Primitive::String),
));
assert!(matches!(
returns[1].data_type.concrete_type(),
Types::Primitive(Primitive::Bool),
));
}
#[test]
fn cannot_redefine_parameters() {
let slice = "
module Test
interface I {
op(a: bool, a: int32)
}
";
let diagnostics = parse_for_diagnostics(slice);
let expected = Diagnostic::new(Error::Redefinition {
identifier: "a".to_string(),
})
.add_note("'a' was previously defined here", None);
check_diagnostics(diagnostics, [expected]);
}
#[test]
fn cannot_redefine_return_members() {
let slice = "
module Test
interface I {
op() -> (a: string, a: int8)
}
";
let diagnostics = parse_for_diagnostics(slice);
let expected = Diagnostic::new(Error::Redefinition {
identifier: "a".to_string(),
})
.add_note("'a' was previously defined here", None);
check_diagnostics(diagnostics, [expected]);
}
#[test_case("()"; "0 elements")]
#[test_case("(b: bool)"; "1 element")]
fn return_tuple_must_contain_two_or_more_elements(return_tuple: &str) {
let slice = format!(
"
module Test
interface I {{
op() -> {return_tuple}
}}
"
);
let diagnostics = parse_for_diagnostics(slice);
let expected = Diagnostic::new(Error::ReturnTuplesMustContainAtLeastTwoElements);
check_diagnostics(diagnostics, [expected]);
}
mod slice2 {
use crate::test_helpers::*;
use slicec::diagnostics::{Diagnostic, Error};
#[test]
fn exception_specifications_are_not_supported() {
let slice1 = "
mode = Slice1
module Test
exception E {}
";
let slice2 = "
mode = Slice2
module Test
interface I {
op() throws E
}
";
let diagnostics = parse_multiple_for_diagnostics(&[slice1, slice2]);
let expected = Diagnostic::new(Error::ExceptionSpecificationNotSupported);
check_diagnostics(diagnostics, [expected]);
}
}
mod slice1 {
use crate::test_helpers::*;
use slicec::diagnostics::{Diagnostic, Error};
use slicec::grammar::{NamedSymbol, Operation};
#[test]
fn operations_can_omit_throws_clause() {
let slice = "
mode = Slice1
module Test
exception E1 {}
exception E2 {}
interface I {
op()
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
assert!(operation.exception_specification.is_empty());
}
#[test]
fn operations_can_throw_single_exception() {
let slice = "
mode = Slice1
module Test
exception E1 {}
exception E2 {}
interface I {
op() throws E1
}
";
let ast = parse_for_ast(slice);
let op = ast.find_element::<Operation>("Test::I::op").unwrap();
let single_exception = &op.exception_specification[0];
assert_eq!(single_exception.parser_scoped_identifier(), "Test::E1");
}
#[test]
fn operations_can_throw_multiple_exceptions() {
let slice = "
mode = Slice1
module Test
exception E1 {}
exception E2 {}
interface I {
op() throws (E1, E2)
}
";
let ast = parse_for_ast(slice);
let op = ast.find_element::<Operation>("Test::I::op").unwrap();
let first_exception = &op.exception_specification[0];
assert_eq!(first_exception.parser_scoped_identifier(), "Test::E1");
let second_exception = &op.exception_specification[1];
assert_eq!(second_exception.parser_scoped_identifier(), "Test::E2");
}
#[test]
fn operations_can_only_throw_exceptions() {
let slice = "
module Test
struct S {}
interface I {
op() throws S
}
";
let diagnostics = parse_for_diagnostics(slice);
let expected = Diagnostic::new(Error::TypeMismatch {
expected: "exception".to_owned(),
actual: "struct".to_owned(),
is_concrete: true,
});
check_diagnostics(diagnostics, [expected]);
}
}
mod streams {
use crate::test_helpers::*;
use slicec::diagnostics::{Diagnostic, Error};
use slicec::grammar::*;
#[test]
fn can_have_streamed_parameter_and_return() {
let slice = "
module Test
interface I {
op(a: stream uint32) -> stream uint32
}
";
let ast = parse_for_ast(slice);
let operation = ast.find_element::<Operation>("Test::I::op").unwrap();
let parameters = operation.parameters();
let returns = operation.return_members();
assert!(parameters[0].is_streamed);
assert!(returns[0].is_streamed);
}
#[test]
fn operation_can_have_at_most_one_streamed_parameter() {
let slice = "
module Test
interface I {
op(s: stream varuint62, s2: stream string)
}
";
let diagnostics = parse_for_diagnostics(slice);
let expected = [
Diagnostic::new(Error::StreamedMembersMustBeLast {
parameter_identifier: "s".to_owned(),
}),
Diagnostic::new(Error::MultipleStreamedMembers),
];
check_diagnostics(diagnostics, expected);
}
#[test]
fn stream_parameter_must_be_last() {
let slice = "
module Test
interface I {
op(s: stream varuint62, i: int32)
}
";
let diagnostics = parse_for_diagnostics(slice);
let expected = Diagnostic::new(Error::StreamedMembersMustBeLast {
parameter_identifier: "s".to_owned(),
});
check_diagnostics(diagnostics, [expected]);
}
}