Skip to content

Commit 5b67d3f

Browse files
feat: test binop and delete expr
1 parent c0ffeea commit 5b67d3f

File tree

6 files changed

+286
-12
lines changed

6 files changed

+286
-12
lines changed

crates/forge/src/mutation/mutant.rs

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ impl Display for UnaryOpMutated {
2727
}
2828
}
2929

30+
// @todo add a mutation from universalmutator: line swap (swap two lines of code, as it
31+
// could theoretically uncover untested reentrancies
3032
#[derive(Debug)]
3133
pub enum MutationType {
3234
// @todo Solar doesn't differentiate numeric type in LitKind (only on declaration?) -> for

crates/forge/src/mutation/mutators/tests/assignement_mutator_test.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::mutation::{
22
mutant::{Mutant, MutationType},
3-
mutators::{assignement_mutator::AssignmentMutator, MutationContext, Mutator},
3+
mutators::{
4+
assignement_mutator::AssignmentMutator, tests::helper::*, MutationContext, Mutator,
5+
},
46
visitor::AssignVarTypes,
57
Session,
68
};
@@ -14,10 +16,6 @@ use solar_parse::{
1416

1517
use super::*;
1618

17-
fn create_span(start: u32, end: u32) -> Span {
18-
Span::new(BytePos(start), BytePos(end))
19-
}
20-
2119
#[test]
2220
fn test_is_applicable_for_assign_expr() {
2321
let arena = Arena::new();
@@ -166,7 +164,7 @@ fn test_generate_identifier_mutants() {
166164

167165
let _ = sess.enter(|| -> solar_parse::interface::Result<()> {
168166
let expr = arena.alloc(Expr {
169-
kind: ExprKind::Ident(Ident { name: Symbol::intern("varia"), span }),
167+
kind: ExprKind::Ident(Ident { name: Symbol::intern("variable"), span }),
170168
span,
171169
});
172170

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use crate::mutation::{
2+
mutant::{Mutant, MutationType},
3+
mutators::{binary_op_mutator::BinaryOpMutator, tests::helper::*, MutationContext, Mutator},
4+
visitor::AssignVarTypes,
5+
Session,
6+
};
7+
use solar_parse::{
8+
ast::{
9+
Arena, BinOp, BinOpKind, ElementaryType, Expr, ExprKind, Ident, Lit, LitKind, Span, Symbol,
10+
Type, TypeKind, VariableDefinition,
11+
},
12+
interface::BytePos,
13+
};
14+
15+
use super::*;
16+
17+
#[test]
18+
fn test_is_applicable_for_binary_expr() {
19+
let arena = Arena::new();
20+
let span = create_span(10, 20);
21+
22+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
23+
let mut val2 = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(45.into()) };
24+
25+
let left = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
26+
27+
let right = arena.alloc(Expr { kind: ExprKind::Lit(&mut val2, None), span });
28+
29+
let bin_op = BinOp { span, kind: BinOpKind::Add };
30+
31+
let expr = arena.alloc(Expr { kind: ExprKind::Binary(left, bin_op, right), span });
32+
33+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
34+
35+
let mutator = BinaryOpMutator;
36+
assert!(mutator.is_applicable(&context));
37+
}
38+
39+
#[test]
40+
fn test_is_applicable_for_assign_expr() {
41+
let arena = Arena::new();
42+
let span = create_span(10, 20);
43+
44+
let left =
45+
arena.alloc(Expr { kind: ExprKind::Ident(Ident { name: Symbol::DUMMY, span }), span });
46+
47+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
48+
49+
let right = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
50+
51+
let bin_op = BinOp { span, kind: BinOpKind::Add };
52+
53+
let expr = arena.alloc(Expr { kind: ExprKind::Assign(left, Some(bin_op), right), span });
54+
55+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
56+
57+
let mutator = BinaryOpMutator;
58+
assert!(mutator.is_applicable(&context));
59+
}
60+
61+
#[test]
62+
fn test_is_not_applicable_assign_without_binary_op() {
63+
let arena = Arena::new();
64+
65+
let span = create_span(10, 20);
66+
67+
let left =
68+
arena.alloc(Expr { kind: ExprKind::Ident(Ident { name: Symbol::DUMMY, span }), span });
69+
70+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
71+
72+
let right = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
73+
74+
let expr = arena.alloc(Expr { kind: ExprKind::Assign(left, None, right), span });
75+
76+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
77+
78+
let mutator = BinaryOpMutator;
79+
assert!(!mutator.is_applicable(&context));
80+
}
81+
82+
#[test]
83+
fn test_generate_arithmetic_mutants() {
84+
let arena = Arena::new();
85+
let span = create_span(10, 20);
86+
87+
let sess = Session::builder().with_silent_emitter(None).build();
88+
89+
let _ = sess.enter(|| -> solar_parse::interface::Result<()> {
90+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
91+
let mut val2 = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(45.into()) };
92+
93+
let left = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
94+
95+
let right = arena.alloc(Expr { kind: ExprKind::Lit(&mut val2, None), span });
96+
97+
let bin_op = BinOp { span, kind: BinOpKind::Add };
98+
99+
let expr = arena.alloc(Expr { kind: ExprKind::Binary(left, bin_op, right), span });
100+
101+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
102+
103+
let mutator = BinaryOpMutator;
104+
let mutants = mutator.generate_mutants(&context).unwrap();
105+
106+
let operations_num_bitwise = vec![
107+
// Arithm
108+
BinOpKind::Shr,
109+
BinOpKind::Shl,
110+
BinOpKind::Sar,
111+
BinOpKind::BitAnd,
112+
BinOpKind::BitOr,
113+
BinOpKind::BitXor,
114+
BinOpKind::Add,
115+
BinOpKind::Sub,
116+
BinOpKind::Pow,
117+
BinOpKind::Mul,
118+
BinOpKind::Div,
119+
BinOpKind::Rem,
120+
];
121+
122+
assert_eq!(mutants.len(), operations_num_bitwise.len() - 1);
123+
124+
let mutants_kind = mutants
125+
.iter()
126+
.map(|m| match m.mutation {
127+
MutationType::BinaryOp(kind) => kind,
128+
_ => panic!("Expected binary op mutant"),
129+
})
130+
.collect::<Vec<_>>();
131+
132+
assert!(all_but_one(&operations_num_bitwise, &mutants_kind));
133+
134+
Ok(())
135+
});
136+
}
137+
138+
#[test]
139+
fn test_generate_bool_op_mutants() {
140+
let arena = Arena::new();
141+
let span = create_span(10, 20);
142+
143+
let sess = Session::builder().with_silent_emitter(None).build();
144+
145+
let _ = sess.enter(|| -> solar_parse::interface::Result<()> {
146+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
147+
let mut val2 = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(45.into()) };
148+
149+
let left = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
150+
151+
let right = arena.alloc(Expr { kind: ExprKind::Lit(&mut val2, None), span });
152+
153+
let bin_op = BinOp { span, kind: BinOpKind::Lt };
154+
155+
let expr = arena.alloc(Expr { kind: ExprKind::Binary(left, bin_op, right), span });
156+
157+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
158+
159+
let mutator = BinaryOpMutator;
160+
let mutants = mutator.generate_mutants(&context).unwrap();
161+
162+
let operations_bools = vec![
163+
BinOpKind::Lt,
164+
BinOpKind::Le,
165+
BinOpKind::Gt,
166+
BinOpKind::Ge,
167+
BinOpKind::Eq,
168+
BinOpKind::Ne,
169+
BinOpKind::Or,
170+
BinOpKind::And,
171+
];
172+
173+
assert_eq!(mutants.len(), operations_bools.len() - 1);
174+
175+
let mutants_kind = mutants
176+
.iter()
177+
.map(|m| match m.mutation {
178+
MutationType::BinaryOp(kind) => kind,
179+
_ => panic!("Expected binary op mutant"),
180+
})
181+
.collect::<Vec<_>>();
182+
183+
assert!(all_but_one(&operations_bools, &mutants_kind));
184+
185+
Ok(())
186+
});
187+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::mutation::{
2+
mutant::{Mutant, MutationType},
3+
mutators::{
4+
delete_expression_mutator::DeleteExpressionMutator, tests::helper::*, MutationContext,
5+
Mutator,
6+
},
7+
visitor::AssignVarTypes,
8+
Session,
9+
};
10+
use solar_parse::{
11+
ast::{
12+
Arena, BinOp, BinOpKind, ElementaryType, Expr, ExprKind, Ident, Lit, LitKind, Span, Symbol,
13+
Type, TypeKind, VariableDefinition,
14+
},
15+
interface::BytePos,
16+
};
17+
18+
use super::*;
19+
20+
#[test]
21+
fn test_is_applicable_for_delete_expr() {
22+
let arena = Arena::new();
23+
let span = create_span(10, 20);
24+
25+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
26+
27+
let left = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
28+
29+
let expr = arena.alloc(Expr { kind: ExprKind::Delete(left), span });
30+
31+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
32+
33+
let mutator = DeleteExpressionMutator;
34+
assert!(mutator.is_applicable(&context));
35+
}
36+
37+
#[test]
38+
fn test_generate_delete_mutants() {
39+
let arena = Arena::new();
40+
let span = create_span(10, 20);
41+
42+
let mut val = Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Number(23.into()) };
43+
44+
let left = arena.alloc(Expr { kind: ExprKind::Lit(&mut val, None), span });
45+
46+
let expr = arena.alloc(Expr { kind: ExprKind::Delete(left), span });
47+
48+
let context = MutationContext { expr: Some(expr), var_definition: None, span };
49+
50+
let mutator = DeleteExpressionMutator;
51+
let mutants = mutator.generate_mutants(&context).unwrap();
52+
53+
assert_eq!(mutants.len(), 1);
54+
// assert_eq!(mutants[0].mutation, MutationType::DeleteExpression);
55+
56+
if let MutationType::DeleteExpression = &mutants[0].mutation {
57+
assert!(true);
58+
} else {
59+
panic!("Expected delete mutation");
60+
}
61+
}

crates/forge/src/mutation/mutators/tests/ast_helper.rs renamed to crates/forge/src/mutation/mutators/tests/helper.rs

+29-5
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ use crate::mutation::{
22
mutant::{Mutant, MutationType},
33
mutators::{assignement_mutator::AssignmentMutator, MutationContext, Mutator},
44
visitor::AssignVarTypes,
5+
Session,
56
};
7+
use num_bigint::BigInt;
68
use solar_parse::{
79
ast::{Arena, Expr, ExprKind, Ident, Lit, LitKind, Span, Symbol},
810
interface::BytePos,
911
};
10-
11-
use num_bigint::BigInt;
12-
use std::path::PathBuf;
13-
14-
use crate::mutation::Session;
12+
use std::{collections::HashMap, hash::Hash, path::PathBuf};
1513

1614
// Create a span for test use
1715
pub fn create_span(start: u32, end: u32) -> Span {
@@ -32,3 +30,29 @@ pub fn create_number_lit(value: u32, span: Span) -> Lit {
3230
pub fn create_bool_lit(value: bool, span: Span) -> Lit {
3331
Lit { span, symbol: Symbol::DUMMY, kind: LitKind::Bool(value) }
3432
}
33+
34+
pub fn all_but_one<T: Eq + Hash>(theoretic: &[T], observed: &[T]) -> bool {
35+
if theoretic.len() != observed.len() + 1 {
36+
return false;
37+
}
38+
39+
let mut counts = HashMap::new();
40+
41+
for item in theoretic {
42+
*counts.entry(item).or_insert(0) += 1;
43+
}
44+
45+
for item in observed {
46+
if let Some(count) = counts.get_mut(item) {
47+
*count -= 1;
48+
if *count == 0 {
49+
counts.remove(item);
50+
}
51+
} else {
52+
return false; // observed has something not in theoretic
53+
}
54+
}
55+
56+
// Only one item should remain in the map
57+
counts.len() == 1 && counts.values().all(|&v| v == 1)
58+
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
mod assignement_mutator_test;
2-
mod ast_helper;
2+
mod binary_op_mutator_test;
3+
mod delete_expression_mutator_test;
4+
mod helper;

0 commit comments

Comments
 (0)