Skip to content

Commit

Permalink
Do not do combinations, instead just runs every one thing once
Browse files Browse the repository at this point in the history
  • Loading branch information
JordiPolo committed Feb 4, 2020
1 parent 7a6e8bd commit 22fe5d9
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 68 deletions.
156 changes: 97 additions & 59 deletions src/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ impl Mutator {
debug!("QM size {:?}", query_mutations.len());
// TODO: CLI param to do or not do crazy amount of combinations.
//Group by request part or parameter so later can do combinations
// group_by only works well on sorted vectors
for (_key, group) in &query_mutations
.iter()
.sorted_by(|a, b| {
Expand All @@ -147,72 +148,109 @@ impl Mutator {

// TODO: Deal with the explosion in a more elegant way.
// TODO: Report better when an endpoint is not being mutated
if query_params.iter().map(|a| a.len()).product::<usize>() > 1000 {
println!(
"Too many mutations for this endpoint {}.",
endpoint.path_name
);
return scenarios;
}
// if query_params.iter().map(|a| a.len()).product::<usize>() > 100_000 {
// println!(
// "Too many mutations for this endpoint {}.",
// endpoint.path_name
// );
// return scenarios;
// }

// Make a copy of the first item of each vector which is per each requestPart or each parameter
let mut first_query_params = Vec::new();
let mut first_nq = Vec::new();

for q in &query_params {
first_query_params.push(vec![*q.get(0).unwrap()]);
}
// We do not do combinations anymore
let mut combinations = Vec::new();

for q in &non_query_params {
first_nq.push(vec![*q.get(0).unwrap()]);
}
let mut params = non_query_params.clone();
params.append(&mut query_params);

// Now we have a set of arrays "first_nq" which all contain 1 element, this should be a passing
// mutation, we put them together with the rest of the query params.
// Later when these guys combine, we will have combinations limited to only on one side the
// one elemnt and on the other many dimensions if available.
// We do the same for the other side and concat.

//The whole goal of this implementation is to avoid an all-with-all combination which would be wasteful.
query_params.to_vec().append(&mut first_nq);
non_query_params.to_vec().append(&mut first_query_params);

let mut all_things: Vec<Vec<&Mutation>> = non_query_params
.into_iter()
.multi_cartesian_product()
.collect();

debug!("number of all nonquery param {:?}", all_things.len());

let mut combination2: Vec<Vec<&Mutation>> =
query_params.into_iter().multi_cartesian_product().collect();

all_things.append(&mut combination2);

debug!("number of all things {:?}", all_things.len());
debug!(" of all things 1 {:?}", all_things[0]);
for combination in all_things {
let mut expected: Vec<StatusCode> =
combination.iter().map(|m| m.mutagen.expected).collect();
expected.push(StatusCode::OK); // To avoid matching on a combination with only errors
expected.sort();
expected.dedup();

debug!("These are expected {:?}\n", expected);
if expected.len() < 3 {
debug!("new scenario");
// Get a request from the combination of the things
let request = Mutator::request_from_instructions(combination.clone());
// TODO from it create scenario
let scenario = Scenario::new(
endpoint.clone(),
combination.clone().into_iter().cloned().collect(),
request,
);
scenarios.push(scenario);
for i in 0..params.len()-1 {
for j in 1..params[i].len() { //Start from 1 becase we will be choosing the element 0 in inner loop
let mut temp = Vec::new();
for z in 0..params.len()-1 {
if i == z {
continue;
}
temp.push(params[z][0]);
}
temp.push(params[i][j]);
combinations.push(temp);
}
}

for combination in combinations {
let request = Mutator::request_from_instructions(combination.clone());
let scenario = Scenario::new(
endpoint.clone(),
combination.into_iter().cloned().collect(),
request,
);
scenarios.push(scenario);
}

scenarios

// Make a copy of the first item of each vector which is per each requestPart or each parameter
// let mut first_query_params = Vec::new();
// let mut first_nq = Vec::new();

// for q in &query_params {
// first_query_params.push(vec![*q.get(0).unwrap()]);
// }

// for q in &non_query_params {
// first_nq.push(vec![*q.get(0).unwrap()]);
// }

// // Now we have a set of arrays "first_nq" which all contain 1 element, this should be a passing
// // mutation, we put them together with the rest of the query params.
// // Later when these guys combine, we will have combinations limited to only on one side the
// // one elemnt and on the other many dimensions if available.
// // We do the same for the other side and concat.

// //The whole goal of this implementation is to avoid an all-with-all combination which would be wasteful.
// query_params.to_vec().append(&mut first_nq);
// non_query_params.to_vec().append(&mut first_query_params);

// let mut all_things: Vec<Vec<&Mutation>> = non_query_params
// .into_iter()
// .multi_cartesian_product()
// .collect();

// debug!("number of all nonquery param {:?}", all_things.len());

// let mut combination2: Vec<Vec<&Mutation>> =
// query_params.into_iter().multi_cartesian_product().collect();

// all_things.append(&mut combination2);

// // TODO: Instead of creating huge amount of combinations and then filter, be more intelligent
// debug!("number of all things {:?}", all_things.len());
// debug!(" of all things 1 {:?}", all_things[0]);
// for combination in all_things {
// let erroring = combination.iter().filter(|&m| m.mutagen.expected != StatusCode::OK).count();

// // let mut expected: Vec<StatusCode> =
// // combination.iter().map(|m| m.mutagen.expected).collect();
// // expected.push(StatusCode::OK); // To avoid matching on a combination with only errors
// // expected.sort();
// // expected.dedup();

// // debug!("These are expected {:?}\n", expected);
// // if expected.len() < 3 {
// if erroring <= 1 {
// debug!("new scenario");
// // Get a request from the combination of the things
// let request = Mutator::request_from_instructions(combination.clone());
// // TODO from it create scenario
// let scenario = Scenario::new(
// endpoint.clone(),
// combination.clone().into_iter().cloned().collect(),
// request,
// );
// scenarios.push(scenario);
// }
// }
// scenarios
}

fn request_from_instructions(mutations: Vec<&Mutation>) -> Request {
Expand Down
6 changes: 3 additions & 3 deletions src/mutation/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ impl MutagenInstruction {
}
}
fn new_with_list(tuple: (RequestPart, StatusCode, Vec<Mutagen>)) -> Vec<Self> {
let (request_part, status_code, mutagens) = tuple;
let (request_part, expected, mutagens) = tuple;
mutagens
.into_iter()
.map(|mutagen| MutagenInstruction {
request_part: request_part.clone(),
mutagen: mutagen,
expected: status_code,
mutagen,
expected,
})
.collect()
}
Expand Down
14 changes: 8 additions & 6 deletions src/mutation/string_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::mutation::param_mutation::ParamMutation;
use crate::mutation::Mutagen;
use chrono::prelude::*;
use rand::seq::SliceRandom;

// TODO String type has "pattern"
pub fn mutate(param: &openapiv3::Parameter, string_type: &openapiv3::StringType) -> ParamMutation {
Expand All @@ -13,12 +12,15 @@ pub fn mutate(param: &openapiv3::Parameter, string_type: &openapiv3::StringType)
// mutations.push(&long_string, Mutagen::HugelyLongString);

if !string_type.enumeration.is_empty() {
// Proper enum value
let mut rng = rand::thread_rng();
let value = string_type.enumeration.choose(&mut rng).unwrap();
mutations.push(value, Mutagen::EnumerationElement);
// Improper enum value
for element in &string_type.enumeration {
mutations.push(&element, Mutagen::EnumerationElement);
if !element.chars().all(char::is_uppercase) {
mutations.push(&element.to_uppercase(), Mutagen::NotEnumerationElement);
}
}
mutations.push("", Mutagen::NotEnumerationElement);
mutations.push("NotInAnyEnum", Mutagen::NotEnumerationElement);

} else if string_type.format == openapiv3::VariantOrUnknownOrEmpty::Empty {
if let Some(min) = string_type.min_length {
if min > 1 {
Expand Down
1 change: 1 addition & 0 deletions src/reporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub fn run_summary(results: &Vec<(String, bool)>, start: std::time::Instant) {
}

println!("{}", table);
println!("Consider using a conversions file to increase the coverage of endpoints with required parameters.");

println!(
"{} scenarios executed in {:?}.\n {:?} passed, {:?} failed.",
Expand Down

0 comments on commit 22fe5d9

Please sign in to comment.