use monty::MontyRun;
use monty_types::{CompileOptions, MontyException, UnicodeErrorData, UnicodeErrorObject};
fn run_err(code: &str) -> String {
run_exc(code).to_string()
}
fn run_exc(code: &str) -> MontyException {
MontyRun::new(code.to_owned(), "test.py", vec![], CompileOptions::default())
.unwrap()
.run_no_limits(vec![])
.unwrap_err()
}
fn run_str(code: &str) -> String {
let result = MontyRun::new(code.to_owned(), "test.py", vec![], CompileOptions::default())
.unwrap()
.run_no_limits(vec![])
.unwrap();
result.as_ref().try_into().unwrap()
}
#[test]
fn decode_surrogateescape_reports_not_implemented() {
insta::assert_snapshot!(run_err("b'h\\xe9'.decode('ascii', 'surrogateescape')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
b'h\xe9'.decode('ascii', 'surrogateescape')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotImplementedError: the 'surrogateescape' error handler is not supported by Monty for decoding: Monty strings cannot contain the lone surrogate characters it produces
"#);
}
#[test]
fn utf8_surrogatepass_cesu8_reports_not_implemented() {
insta::assert_snapshot!(run_err("b'\\xed\\xa0\\x80'.decode('utf-8', 'surrogatepass')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
b'\xed\xa0\x80'.decode('utf-8', 'surrogatepass')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotImplementedError: the 'surrogatepass' error handler is not supported by Monty for decoding: Monty strings cannot contain the lone surrogate characters it produces
"#);
}
#[test]
fn utf16_surrogate_handlers_report_not_implemented() {
insta::assert_snapshot!(run_err("b'\\x00\\xd8'.decode('utf-16-le', 'surrogateescape')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
b'\x00\xd8'.decode('utf-16-le', 'surrogateescape')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotImplementedError: the 'surrogateescape' error handler is not supported by Monty for decoding: Monty strings cannot contain the lone surrogate characters it produces
"#);
insta::assert_snapshot!(run_err("b'\\x00\\xd8'.decode('utf-16-le', 'surrogatepass')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
b'\x00\xd8'.decode('utf-16-le', 'surrogatepass')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotImplementedError: the 'surrogatepass' error handler is not supported by Monty for decoding: Monty strings cannot contain the lone surrogate characters it produces
"#);
}
#[test]
fn utf32_surrogatepass_reports_not_implemented() {
insta::assert_snapshot!(run_err("b'\\x00\\xd8\\x00\\x00'.decode('utf-32-le', 'surrogatepass')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
b'\x00\xd8\x00\x00'.decode('utf-32-le', 'surrogatepass')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotImplementedError: the 'surrogatepass' error handler is not supported by Monty for decoding: Monty strings cannot contain the lone surrogate characters it produces
"#);
}
#[test]
fn bomless_bare_utf16_utf32_decode_defaults_to_little_endian() {
assert_eq!(run_str("b'a\\x00'.decode('utf-16')"), "a");
assert_eq!(run_str("b'a\\x00\\x00\\x00'.decode('utf-32')"), "a");
}
#[test]
fn unicode_decode_error_carries_structured_data() {
let exc = run_exc("b'a\\xffb'.decode()");
assert_eq!(
exc.unicode_data(),
Some(&UnicodeErrorData {
encoding: "utf-8".to_owned(),
object: UnicodeErrorObject::Bytes(b"a\xffb".to_vec()),
start: 1,
end: 2,
reason: "invalid start byte".to_owned(),
})
);
}
#[test]
fn unicode_encode_error_carries_structured_data() {
let exc = run_exc("'caf\\xe9'.encode('ascii')");
assert_eq!(
exc.unicode_data(),
Some(&UnicodeErrorData {
encoding: "ascii".to_owned(),
object: UnicodeErrorObject::Str("café".to_owned()),
start: 3,
end: 4,
reason: "ordinal not in range(128)".to_owned(),
})
);
}
#[test]
fn unicode_error_data_survives_reraise() {
let exc = run_exc("try:\n b'\\xff'.decode()\nexcept ValueError as e:\n raise e");
assert_eq!(exc.exc_type().to_string(), "UnicodeDecodeError");
assert!(exc.unicode_data().is_some());
}
#[test]
fn unicode_error_data_omitted_for_huge_objects() {
let exc = run_exc("(b'a' * 100_000 + b'\\xff').decode()");
assert_eq!(exc.exc_type().to_string(), "UnicodeDecodeError");
assert_eq!(exc.unicode_data(), None);
assert_eq!(
exc.message(),
Some("'utf-8' codec can't decode byte 0xff in position 100000: invalid start byte")
);
}
#[test]
fn latin1_reports_unknown_encoding() {
insta::assert_snapshot!(run_err("'hi'.encode('latin-1')"), @r#"
Traceback (most recent call last):
File "test.py", line 1, in <module>
'hi'.encode('latin-1')
~~~~~~~~~~~~~~~~~~~~~~
LookupError: unknown encoding: latin-1
"#);
}