use super::super::intern::{intern, intern_uninterned, intern_uninterned_lisp_string};
use super::super::marker::make_marker_value;
use super::*;
use crate::emacs_core::value::{
HashTableTest, LambdaData, LambdaParams, StringTextPropertyRun, next_float_id,
};
#[test]
fn print_basic_values() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::NIL), "nil");
assert_eq!(print_value(&Value::T), "t");
assert_eq!(print_value(&Value::fixnum(42)), "42");
assert_eq!(print_value(&Value::make_float(3.14)), "3.14");
assert_eq!(print_value(&Value::make_float(1.0)), "1.0");
assert_eq!(print_value(&Value::symbol("foo")), "foo");
assert_eq!(print_value(&Value::symbol(".foo")), "\\.foo");
assert_eq!(print_value(&Value::symbol("")), "##");
assert_eq!(print_value(&Value::keyword(":bar")), ":bar");
}
#[test]
fn print_symbol_escapes_reader_sensitive_chars() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::symbol("a b")), "a\\ b");
assert_eq!(print_value(&Value::symbol("a,b")), "a\\,b");
assert_eq!(print_value(&Value::symbol("a,@b")), "a\\,@b");
assert_eq!(print_value(&Value::symbol("a#b")), "a\\#b");
assert_eq!(print_value(&Value::symbol("a'b")), "a\\'b");
assert_eq!(print_value(&Value::symbol("a`b")), "a\\`b");
assert_eq!(print_value(&Value::symbol("a\\b")), "a\\\\b");
assert_eq!(print_value(&Value::symbol("a\"b")), "a\\\"b");
assert_eq!(print_value(&Value::symbol("a(b")), "a\\(b");
assert_eq!(print_value(&Value::symbol("a)b")), "a\\)b");
assert_eq!(print_value(&Value::symbol("a[b")), "a\\[b");
assert_eq!(print_value(&Value::symbol("a]b")), "a\\]b");
assert_eq!(print_value(&Value::symbol("##")), "\\#\\#");
assert_eq!(print_value(&Value::symbol("?a")), "\\?a");
assert_eq!(print_value(&Value::symbol("a?b")), "a?b");
}
#[test]
fn print_uninterned_symbols_follow_gnu_default_print_gensym_nil() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::symbol(intern_uninterned("foo"))), "foo");
assert_eq!(
print_value(&Value::symbol(intern_uninterned(":foo"))),
":foo"
);
assert_eq!(print_value(&Value::symbol(intern_uninterned(""))), "##");
}
#[test]
fn print_raw_unibyte_uninterned_symbol_bytes_match_gnu_encoding() {
crate::test_utils::init_test_tracing();
let raw_name = crate::heap_types::LispString::from_unibyte(vec![0xFF, b'a']);
let sym = Value::symbol(intern_uninterned_lisp_string(&raw_name));
assert_eq!(print_value_bytes(&sym), vec![0xC1, 0xBF, b'a']);
}
#[test]
fn print_uninterned_symbols_support_print_gensym_round_trip_syntax() {
crate::test_utils::init_test_tracing();
let options = PrintOptions::with_print_gensym(true);
assert_eq!(
print_value_with_options(&Value::symbol(intern_uninterned("foo")), options),
"#:foo"
);
assert_eq!(
print_value_with_options(&Value::symbol(intern_uninterned(":foo")), options),
"#::foo"
);
assert_eq!(
print_value_with_options(&Value::symbol(intern_uninterned("")), options),
"#:"
);
}
#[test]
fn print_gensym_raw_unibyte_symbol_bytes_match_gnu_encoding() {
crate::test_utils::init_test_tracing();
let options = PrintOptions::with_print_gensym(true);
let raw_name = crate::heap_types::LispString::from_unibyte(vec![0xFF, b'a']);
let sym = Value::symbol(intern_uninterned_lisp_string(&raw_name));
assert_eq!(
print_value_bytes_with_options(&sym, options),
vec![b'#', b':', 0xC1, 0xBF, b'a']
);
}
#[test]
fn print_float_nan_preserves_sign() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::make_float(f64::NAN)), "0.0e+NaN");
let neg_nan = f64::from_bits(f64::NAN.to_bits() | (1_u64 << 63));
assert_eq!(print_value(&Value::make_float(neg_nan)), "-0.0e+NaN");
}
#[test]
fn print_float_nan_payload_tag_round_trip_shape() {
crate::test_utils::init_test_tracing();
let tagged = f64::from_bits((0x7ffu64 << 52) | (1u64 << 51) | 1u64);
assert_eq!(print_value(&Value::make_float(tagged)), "1.0e+NaN");
let neg_tagged = f64::from_bits((1u64 << 63) | (0x7ffu64 << 52) | (1u64 << 51) | 2u64);
assert_eq!(print_value(&Value::make_float(neg_tagged)), "-2.0e+NaN");
}
#[test]
fn print_string() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::string("hello")), "\"hello\"");
}
#[test]
fn print_empty_char_table_uses_gnu_vector_shape() {
crate::test_utils::init_test_tracing();
let table = crate::emacs_core::chartable::make_char_table_with_extra_slots(
Value::symbol("syntax-table"),
Value::NIL,
0,
);
let rendered = print_value(&table);
assert!(rendered.starts_with("#^[nil nil syntax-table"));
}
#[test]
fn print_propertized_string_literal_shape() {
crate::test_utils::init_test_tracing();
let value = Value::string_with_text_properties(
" ",
vec![StringTextPropertyRun {
start: 0,
end: 1,
plist: Value::list(vec![
Value::symbol("display"),
Value::list(vec![
Value::symbol("space"),
Value::keyword(":align-to"),
Value::list(vec![
Value::symbol("+"),
Value::symbol("header-line-indent-width"),
Value::fixnum(0),
]),
]),
]),
}],
);
assert_eq!(
print_value(&value),
r##"#(" " 0 1 (display (space :align-to (+ header-line-indent-width 0))))"##
);
assert_eq!(
print_value_bytes(&value),
br#"#(" " 0 1 (display (space :align-to (+ header-line-indent-width 0))))"#
);
}
#[test]
fn print_string_keeps_non_bmp_visible() {
crate::test_utils::init_test_tracing();
assert_eq!(print_value(&Value::string("\u{10ffff}")), "\"\u{10ffff}\"");
}
#[test]
fn print_string_bytes_preserve_non_utf8_payloads() {
crate::test_utils::init_test_tracing();
assert_eq!(
print_value_bytes(&Value::heap_string(
crate::heap_types::LispString::from_emacs_bytes(vec![0xC1, 0xBF],)
)),
b"\"\\377\""
);
}
#[test]
fn print_literal_private_use_unicode_does_not_masquerade_as_raw_byte() {
crate::test_utils::init_test_tracing();
let private_use = char::from_u32(0xE0FF).expect("private use scalar");
assert_eq!(
print_value_bytes(&Value::string(private_use.to_string())),
format!("\"{}\"", private_use).into_bytes()
);
}
#[test]
fn print_list() {
crate::test_utils::init_test_tracing();
let lst = Value::list(vec![Value::fixnum(1), Value::fixnum(2), Value::fixnum(3)]);
assert_eq!(print_value(&lst), "(1 2 3)");
}
#[test]
fn print_stateful_record_preprocess_uses_record_storage() {
crate::test_utils::init_test_tracing();
let record = Value::make_record(vec![Value::symbol("foo"), Value::fixnum(1)]);
let options = PrintOptions::new(false, false, Some(10), None);
assert_eq!(print_value_stateful(&record, options), "#s(foo 1)");
}
#[test]
fn print_hash_s_literal_shorthand() {
crate::test_utils::init_test_tracing();
let literal = Value::list(vec![
Value::symbol("make-hash-table-from-literal"),
Value::list(vec![
Value::symbol("quote"),
Value::list(vec![Value::symbol("x")]),
]),
]);
assert_eq!(print_value(&literal), "#s(x)");
assert_eq!(print_value_bytes(&literal), b"#s(x)");
}
#[test]
fn print_hash_table_object_uses_readable_hash_s_shape() {
crate::test_utils::init_test_tracing();
let table = Value::hash_table(HashTableTest::Equal);
assert_eq!(print_value(&table), "#s(hash-table test equal)");
assert_eq!(print_value_bytes(&table), b"#s(hash-table test equal)");
}
#[test]
fn print_quote_shorthand_lists() {
crate::test_utils::init_test_tracing();
let quoted = Value::list(vec![Value::symbol("quote"), Value::symbol("foo")]);
let function = Value::list(vec![Value::symbol("function"), Value::symbol("car")]);
let quasiquoted = Value::list(vec![
Value::symbol("`"),
Value::list(vec![Value::symbol("a"), Value::symbol("b")]),
]);
let unquoted = Value::list(vec![Value::symbol(","), Value::symbol("x")]);
let unquote_splice = Value::list(vec![Value::symbol(",@"), Value::symbol("xs")]);
assert_eq!(print_value("ed), "'foo");
assert_eq!(print_value(&function), "#'car");
assert_eq!(print_value(&quasiquoted), "`(a b)");
assert_eq!(print_value(&unquoted), "(\\, x)");
assert_eq!(print_value(&unquote_splice), "(\\,@ xs)");
}
#[test]
fn print_backquote_preserves_nested_unquote_shorthand_only_in_context() {
crate::test_utils::init_test_tracing();
let nested = Value::list(vec![
Value::symbol("`"),
Value::list(vec![
Value::symbol("a"),
Value::list(vec![Value::symbol(","), Value::symbol("x")]),
]),
]);
assert_eq!(print_value(&nested), "`(a ,x)");
}
#[test]
fn print_dotted_pair() {
crate::test_utils::init_test_tracing();
let pair = Value::cons(Value::fixnum(1), Value::fixnum(2));
assert_eq!(print_value(&pair), "(1 . 2)");
}
#[test]
fn print_vector() {
crate::test_utils::init_test_tracing();
let v = Value::vector(vec![Value::fixnum(1), Value::fixnum(2)]);
assert_eq!(print_value(&v), "[1 2]");
}
#[test]
fn print_default_handles_circular_vector_like_gnu() {
crate::test_utils::init_test_tracing();
let vector = Value::vector(vec![Value::NIL]);
assert!(vector.set_vector_slot(0, vector));
assert_eq!(print_value(&vector), "[#0]");
assert_eq!(print_value_bytes(&vector), b"[#0]");
}
#[test]
fn print_default_handles_circular_cons_like_gnu() {
crate::test_utils::init_test_tracing();
let cell = Value::cons(Value::NIL, Value::NIL);
cell.set_cdr(cell);
assert_eq!(print_value(&cell), "(nil . #0)");
assert_eq!(print_value_bytes(&cell), b"(nil . #0)");
}
#[test]
fn print_circle_handles_self_referential_records() {
crate::test_utils::init_test_tracing();
let record = Value::make_record(vec![Value::symbol("foo"), Value::NIL]);
record.with_record_data_mut(|slots| slots[1] = record);
let options = PrintOptions::new(false, true, None, None);
assert_eq!(
print_value_stateful_with_buffers(&record, None, options),
"#1=#s(foo #1#)"
);
}
#[test]
fn print_default_handles_self_referential_records_like_gnu() {
crate::test_utils::init_test_tracing();
let record = Value::make_record(vec![Value::symbol("foo"), Value::NIL]);
record.with_record_data_mut(|slots| slots[1] = record);
assert_eq!(print_value(&record), "#s(foo #0)");
assert_eq!(print_value_bytes(&record), b"#s(foo #0)");
}
#[test]
fn print_default_handles_self_referential_bytecode_constants() {
crate::test_utils::init_test_tracing();
let mut function =
crate::emacs_core::bytecode::ByteCodeFunction::new(LambdaParams::simple(vec![]));
function.constants.push(Value::NIL);
let bytecode = Value::make_bytecode(function);
bytecode.with_bytecode_data_mut(|data| data.constants[0] = bytecode);
assert_eq!(print_value(&bytecode), "#[nil nil [#0] 0 nil]");
assert_eq!(print_value_bytes(&bytecode), b"#[nil nil [#0] 0 nil]");
let options = PrintOptions::new(false, true, None, None);
assert_eq!(
print_value_with_options(&bytecode, options),
"#1=#[nil nil [#1#] 0 nil]"
);
}
#[test]
fn print_lambda() {
crate::test_utils::init_test_tracing();
let lam = Value::make_lambda(LambdaData {
params: LambdaParams::simple(vec![intern("x"), intern("y")]),
body: vec![Value::list(vec![
Value::symbol("+"),
Value::symbol("x"),
Value::symbol("y"),
])],
env: None,
docstring: None,
doc_form: None,
interactive: None,
});
assert_eq!(print_value(&lam), "(lambda (x y) (+ x y))");
}
#[test]
fn print_lexical_closure_uses_gnu_vector_syntax() {
crate::test_utils::init_test_tracing();
let closure = Value::make_lambda(LambdaData {
params: LambdaParams::simple(vec![intern("a"), intern("b")]),
body: vec![Value::list(vec![
Value::symbol("+"),
Value::symbol("a"),
Value::symbol("b"),
Value::symbol("x"),
])],
env: Some(Value::list(vec![Value::cons(
Value::symbol("x"),
Value::fixnum(42),
)])),
docstring: None,
doc_form: None,
interactive: None,
});
assert_eq!(print_value(&closure), "#[(a b) ((+ a b x)) ((x . 42))]");
assert_eq!(
String::from_utf8(print_value_bytes(&closure)).expect("utf8"),
"#[(a b) ((+ a b x)) ((x . 42))]"
);
}
#[test]
fn print_recursive_closure_uses_backreference() {
crate::test_utils::init_test_tracing();
let binding = Value::cons(Value::symbol("f"), Value::NIL);
let env = Value::list(vec![binding]);
let closure = Value::make_lambda(LambdaData {
params: LambdaParams::simple(vec![]),
body: vec![Value::symbol("f")],
env: Some(env),
docstring: None,
doc_form: None,
interactive: None,
});
binding.set_cdr(closure);
assert_eq!(print_value(&closure), "#[nil (f) ((f . #0))]");
assert_eq!(
String::from_utf8(print_value_bytes(&closure)).expect("utf8"),
"#[nil (f) ((f . #0))]"
);
}
#[test]
fn print_terminal_handle_special_form() {
crate::test_utils::init_test_tracing();
let list = super::super::terminal::pure::builtin_terminal_list(vec![]).unwrap();
let items = list_to_vec(&list).expect("terminal-list should return a list");
let handle = items
.first()
.expect("terminal-list should contain one handle");
let printed = print_value(handle);
assert!(printed.starts_with("#<terminal "));
assert!(printed.contains("on initial_terminal>"));
}
#[test]
fn print_frame_handles_use_oracle_style_f_prefix() {
crate::test_utils::init_test_tracing();
let f1 = Value::make_frame(crate::window::FRAME_ID_BASE);
let f2 = Value::make_frame(crate::window::FRAME_ID_BASE + 1);
let legacy = Value::make_frame(7);
assert_eq!(print_value(&f1), "#<frame F1 0x100000000>");
assert_eq!(print_value_bytes(&f1), b"#<frame F1 0x100000000>");
assert_eq!(print_value(&f2), "#<frame F2 0x100000001>");
assert_eq!(print_value_bytes(&f2), b"#<frame F2 0x100000001>");
assert_eq!(print_value(&legacy), "#<frame 7>");
}
#[test]
fn print_markers_use_gnu_style_handles() {
crate::test_utils::init_test_tracing();
let marker = make_marker_value(None, None, false);
assert_eq!(print_value(&marker), "#<marker in no buffer>");
let mut buffers = crate::buffer::BufferManager::new();
let buffer_id = buffers
.find_buffer_by_name("*scratch*")
.expect("scratch buffer");
let marker = make_marker_value(Some(buffer_id), Some(3), false);
assert_eq!(
print_value_with_buffers(&marker, &buffers),
"#<marker at 3 in *scratch*>"
);
buffers.kill_buffer(buffer_id);
assert_eq!(
print_value_with_buffers(&marker, &buffers),
"#<marker in no buffer>"
);
}