use super::*;
use crate::paths::Lang;
fn scores(lang: Lang, code: &str, max: f64) -> Vec<AbcOffense> {
all_scores(
code.as_bytes(),
&crate::paths::parse_file_lang(code.as_bytes(), lang).unwrap(),
lang,
)
.into_iter()
.filter(|o| o.score > max)
.collect()
}
#[test]
fn javascript_counts_assignments_calls_and_conditions() {
let code = "\
function checkout(user, items) {
let total = 0;
for (const it of items) {
total = total + it.price;
}
if (user.vip) {
total = applyDiscount(total);
}
log(total);
}
";
let off = scores(Lang::Js, code, 0.0);
assert_eq!(off.len(), 1);
assert_eq!(off[0].name, "checkout");
assert_eq!(off[0].vector, "<3, 3, 2>");
assert!(scores(Lang::Js, code, 17.0).is_empty());
}
#[test]
fn ts_const_arrow_is_a_named_unit_and_nested_fns_do_not_double_count() {
let off = scores(
Lang::Ts,
"\
const outer = (xs) => {
function inner(y) {
return helper(y) + helper(y);
}
return inner(1);
};
",
0.0,
);
assert_eq!(off.len(), 2, "{off:?}");
let inner = off.iter().find(|o| o.name == "inner").unwrap();
let outer = off.iter().find(|o| o.name == "outer").unwrap();
assert_eq!(inner.vector, "<0, 3, 0>");
assert_eq!(outer.vector, "<0, 1, 0>");
}
#[test]
fn c_counts_loop_body_with_updates_and_comparisons() {
let off = scores(
Lang::C,
"\
int sum_upto(int n) {
int s = 0;
for (int i = 0; i < n; i++) {
s += i;
}
return s;
}
",
0.0,
);
assert_eq!(off.len(), 1);
assert_eq!(off[0].name, "sum_upto");
assert_eq!(off[0].vector, "<4, 0, 2>");
}
#[test]
fn objective_c_message_sends_are_branches() {
let off = scores(
Lang::ObjC,
"\
@implementation Widget
- (int)pick:(NSArray *)items {
if ([items count] > 0) {
return [[self factory] build];
}
return 0;
}
- (void)foo:(id)x bar:(id)y {
if (x) return;
}
@end
",
0.0,
);
assert_eq!(off.len(), 2, "{off:?}");
assert_eq!(off[0].name, "pick:");
assert_eq!(off[1].name, "foo:bar:");
assert_eq!(off[0].vector, "<0, 3, 2>");
}
#[test]
fn swift_functions_score_on_the_same_scale() {
let off = scores(
Lang::Swift,
"\
func grade(_ score: Int) -> String {
if score >= 90 {
return \"A\"
} else if score >= 80 {
return \"B\"
}
return \"F\"
}
",
0.0,
);
assert_eq!(off.len(), 1);
assert_eq!(off[0].name, "grade");
assert_eq!(off[0].vector, "<0, 0, 4>");
}
#[test]
fn cpp_throw_and_new_are_branches() {
let off = scores(
Lang::Cpp,
"\
Widget* make(int n) {
if (n <= 0) throw std::invalid_argument(\"n\");
return new Widget(n);
}
",
0.0,
);
assert_eq!(off.len(), 1);
assert_eq!(off[0].vector, "<0, 2, 2>");
}
#[test]
fn cpp_out_of_line_method_uses_method_name_not_parameter() {
let names: Vec<_> = scores(
Lang::Cpp,
"\
void Transfers::onGetFileListClicked_gui(GtkMenuItem*, gpointer data) {
Transfers *tr = (Transfers *)data;
if (tr) tr->refresh();
}
const Foo& Foo::get(gpointer data) {
if (data) return *this;
return *this;
}
Foo& Foo::operator=(const Foo& o) {
if (&o == this) return *this;
return *this;
}
Transfers::~Transfers() {
cleanup();
}
Foo::operator int() const {
if (ready) return 1;
return 0;
}
",
0.0,
)
.into_iter()
.map(|o| o.name)
.collect();
assert!(names.iter().any(|n| n == "onGetFileListClicked_gui"), "got {names:?}");
assert!(names.iter().any(|n| n == "get"), "got {names:?}");
assert!(names.iter().any(|n| n == "operator="), "got {names:?}");
assert!(names.iter().any(|n| n == "~Transfers"), "got {names:?}");
assert!(
names.iter().any(|n| n.starts_with("operator int")),
"got {names:?}"
);
assert!(
names.iter().all(|n| n != "data"),
"parameter must not be the unit name"
);
assert!(
names.iter().all(|n| n != "o"),
"parameter must not be the unit name"
);
}