use crate::{parser::*, s_expr::*};
use pretty_assertions::assert_eq;
fn all_option_permutations() -> [SExpressibleOptions; 4]
{
[
SExpressibleOptions::default(),
SExpressibleOptions::default().with_spans(true),
SExpressibleOptions::default().with_groups(true),
SExpressibleOptions::default()
.with_spans(true)
.with_groups(true)
]
}
#[test]
fn test_size_s_expr_matches_single_line_length()
{
use crate::support::read_compilation_test_cases;
let cases = read_compilation_test_cases(include_str!(
"../../../tests/test_parse.txt"
));
assert!(!cases.is_empty(), "no test cases found in test_parse.txt");
for opts_base in all_option_permutations()
{
let opts = SExpressibleOptions::new(
opts_base.indent,
opts_base.tab_width,
10_000
)
.with_spans(opts_base.with_spans)
.with_groups(opts_base.with_groups);
for (index, (source, _)) in cases.iter().enumerate()
{
let ast = Parser::parse(source).unwrap_or_else(|e| {
panic!(
"case {}: parse failed for {:?}: {}",
index + 1,
source,
e
)
});
let serialized = ast.to_s_expr(opts);
assert!(
!serialized.contains('\n'),
"case {} with {:?}: single-line branch expected but output \
wrapped:\n{}",
index + 1,
opts,
serialized
);
assert_eq!(
ast.size_s_expr(opts),
serialized.len(),
"case {} with {:?}: size_s_expr disagrees with rendered \
length for source {:?}\nserialized: {}",
index + 1,
opts,
source,
serialized
);
}
}
}
#[test]
fn test_size_s_expr_empty_parameter_list_matches_written_length()
{
let ast = read_s_expr("(function [] 42)").unwrap();
assert!(ast.parameters.is_none());
let opts = SExpressibleOptions::new(0, 4, 10_000);
let mut buffer = String::new();
ast.parameters
.write_s_expr(&mut buffer, opts.soft_limit, opts)
.unwrap();
assert_eq!(buffer, "[]");
assert_eq!(ast.parameters.size_s_expr(opts), buffer.len());
}
#[test]
fn test_size_s_expr_empty_parameter_slice_matches_written_length()
{
let params: Vec<crate::ast::Parameter<'_>> = Vec::new();
let slice: &[crate::ast::Parameter<'_>] = ¶ms;
let opts = SExpressibleOptions::new(0, 4, 10_000);
let mut buffer = String::new();
slice
.write_s_expr(&mut buffer, opts.soft_limit, opts)
.unwrap();
assert_eq!(buffer, "[]");
assert_eq!(slice.size_s_expr(opts), buffer.len());
}
#[test]
fn test_size_s_expr_integer_faces_matches_written_length()
{
let cases: &[&[i32]] = &[
&[0],
&[1, 2, 3],
&[-1],
&[-1, 0, 1, 3, 5],
&[1, 10, 100, 1000, 10_000, 100_000, 1_000_000],
&[i32::MIN + 1, -1, 0, 1, i32::MAX]
];
let opts = SExpressibleOptions::new(0, 4, 10_000);
for faces in cases
{
let mut buffer = String::new();
faces
.write_s_expr(&mut buffer, opts.soft_limit, opts)
.unwrap();
assert!(
!buffer.contains('\n'),
"single-line branch expected but output wrapped:\n{}",
buffer
);
assert_eq!(
faces.size_s_expr(opts),
buffer.len(),
"size_s_expr disagrees with rendered length for {:?}\n\
serialized: {}",
faces,
buffer
);
}
}
#[test]
fn test_size_s_expr_empty_integer_faces_matches_written_length()
{
let faces: &[i32] = &[];
let opts = SExpressibleOptions::new(0, 4, 10_000);
let mut buffer = String::new();
faces
.write_s_expr(&mut buffer, opts.soft_limit, opts)
.unwrap();
assert_eq!(buffer, "[]");
assert_eq!(faces.size_s_expr(opts), buffer.len());
}
#[test]
fn test_size_s_expr_reference_forwarder_matches_written_length()
{
let ast = Parser::parse("1 + 2").unwrap();
let opts = SExpressibleOptions::new(0, 4, 10_000).with_spans(true);
let by_ref = *
let mut buffer = String::new();
by_ref
.write_s_expr(&mut buffer, opts.soft_limit, opts)
.unwrap();
assert_eq!(by_ref.size_s_expr(opts), buffer.len());
}
#[test]
fn test_increase_indent_steps_by_one()
{
let opts = SExpressibleOptions::new(0, 4, 80)
.with_spans(true)
.with_groups(true);
let step1 = opts.increase_indent();
let step2 = step1.increase_indent();
let step3 = step2.increase_indent();
assert_eq!(step1.indent, 1);
assert_eq!(step2.indent, 2);
assert_eq!(step3.indent, 3);
for stepped in [step1, step2, step3]
{
assert_eq!(stepped.tab_width, 4);
assert_eq!(stepped.soft_limit, 80);
assert!(stepped.with_spans);
assert!(stepped.with_groups);
}
}
#[test]
fn test_available_space_formula()
{
assert_eq!(SExpressibleOptions::new(0, 4, 80).available_space(), 80);
assert_eq!(SExpressibleOptions::new(1, 4, 80).available_space(), 76);
assert_eq!(SExpressibleOptions::new(3, 2, 80).available_space(), 74);
assert_eq!(SExpressibleOptions::new(5, 3, 80).available_space(), 65);
}
#[test]
fn test_span_prefix_size_and_decimal_digits_invariant()
{
for source in ["1", "42", "1000000000", " 100"]
{
let ast = Parser::parse(source).unwrap();
let opts = SExpressibleOptions::default().with_spans(true);
let rendered = ast.to_s_expr(opts);
assert_eq!(
ast.size_s_expr(opts),
rendered.len(),
"size_s_expr disagrees with rendered length for {:?} under \
with_spans:\n{}",
source,
rendered
);
}
}
#[test]
fn test_ident_size_unquoted_and_quoted()
{
let bare = Parser::parse("{alloy}").unwrap();
let opts = SExpressibleOptions::new(0, 4, 10_000);
let rendered = bare.to_s_expr(opts);
assert_eq!(rendered, "(function [] alloy)");
assert_eq!(bare.size_s_expr(opts), rendered.len());
let quoted = Parser::parse("{hello world}").unwrap();
let rendered = quoted.to_s_expr(opts);
assert_eq!(rendered, r#"(function [] "hello world")"#);
assert_eq!(quoted.size_s_expr(opts), rendered.len());
}
#[test]
fn test_identifier_quoting_delimiter_characters()
{
let cases = [
r#"(function [] "v w")"#,
r#"(function [] "v(w")"#,
r#"(function [] "v)w")"#,
r#"(function [] "v[w")"#,
r#"(function [] "v]w")"#
];
let opts = SExpressibleOptions::new(0, 4, 10_000);
for input in cases
{
let ast = read_s_expr(input).unwrap();
let rendered = ast.to_s_expr(opts);
assert_eq!(
ast.size_s_expr(opts),
rendered.len(),
"ident_size disagrees for {:?}: {}",
input,
rendered
);
assert_eq!(
rendered, input,
"writer did not preserve quoted identifier from {:?}: {}",
input, rendered
);
}
}