use super::super::{call_qualifier_path, line_terminator_len, split_qualified_callee};
use super::common::parse_python;
use proptest::prelude::*;
#[test]
fn line_terminator_len_tracks_lf_crlf_and_eof() {
let text = "import 'a';\r\nhttp.Client();\nlast()";
assert_eq!(line_terminator_len(text, 0, "import 'a';".len()), 2);
let second_start = "import 'a';\r\n".len();
assert_eq!(
line_terminator_len(text, second_start, "http.Client();".len()),
1
);
let last_start = "import 'a';\r\nhttp.Client();\n".len();
assert_eq!(line_terminator_len(text, last_start, "last()".len()), 0);
}
#[test]
fn line_terminator_len_matches_generated_suffix_cases() {
let ascii_line = || proptest::string::string_regex("[ -~]{0,64}").expect("valid line regex");
let suffixes = prop_oneof![
Just(String::new()),
Just("\n".to_string()),
Just("\r\n".to_string()),
Just("\r".to_string()),
Just("next".to_string()),
];
let strategy = (ascii_line(), ascii_line(), suffixes);
proptest::test_runner::TestRunner::default()
.run(&strategy, |(prefix, line, suffix)| {
let text = format!("{prefix}{line}{suffix}");
let expected = match suffix.as_str() {
"\r\n" => 2,
"\n" => 1,
_ => 0,
};
let actual = line_terminator_len(&text, prefix.len(), line.len());
assert_eq!(actual, expected);
Ok(())
})
.expect("line terminator property holds");
}
#[test]
fn explicit_qualified_raw_callee_takes_precedence_over_member_prefix() {
let mut inferred_called = false;
let (_, qualifier_from_name) = split_qualified_callee("Vendor\\Pkg\\helper");
let qualifier_path = call_qualifier_path(qualifier_from_name, || {
inferred_called = true;
Some("Vendor".to_string())
});
assert_eq!(qualifier_path.as_deref(), Some("Vendor\\Pkg"));
assert!(!inferred_called);
}
#[test]
fn resolves_unique_same_file_bare_calls() {
let parsed = parse_python(
r#"
def foo():
pass
def bar():
foo()
"#,
&[],
);
let foo = parsed
.symbols
.iter()
.find(|symbol| symbol.name == "foo")
.expect("foo symbol");
let bar = parsed
.symbols
.iter()
.find(|symbol| symbol.name == "bar")
.expect("bar symbol");
let call = parsed.calls.first().expect("call");
assert_eq!(call.caller_symbol_id, bar.id);
assert_eq!(call.callee_symbol_id.as_deref(), Some(foo.id.as_str()));
}
#[test]
fn resolves_same_class_member_calls() {
let parsed = parse_python(
r#"
class Greeter:
def greet(self):
self.render()
def render(self):
pass
"#,
&[],
);
let render = parsed
.symbols
.iter()
.find(|symbol| symbol.qualified_name == "Greeter.render")
.expect("render method");
let call = parsed.calls.first().expect("call");
assert_eq!(call.callee_symbol_id.as_deref(), Some(render.id.as_str()));
}
#[test]
fn leaves_ambiguous_bare_calls_unresolved() {
let parsed = parse_python(
r#"
def foo():
pass
class A:
def foo(self):
pass
def bar():
foo()
"#,
&[],
);
let call = parsed.calls.first().expect("call");
assert!(call.callee_symbol_id.is_none());
}
#[test]
fn leaves_non_local_member_calls_unresolved() {
let parsed = parse_python(
r#"
class A:
def bar(self):
obj.render()
class B:
def render(self):
pass
"#,
&[],
);
let call = parsed.calls.first().expect("call");
assert!(call.callee_symbol_id.is_none());
}