Tweak operation rates

This commit is contained in:
Max Brunsfeld 2023-01-04 18:46:31 -08:00
parent f243633f3e
commit f1b3692a35

View file

@ -230,11 +230,7 @@ async fn test_random_collaboration(
i += 1; i += 1;
} }
Operation::RunUntilParked => { Operation::MutateClients { user_ids, quiesce } => {
deterministic.run_until_parked();
}
Operation::MutateClients(user_ids) => {
for user_id in user_ids { for user_id in user_ids {
let client_ix = clients let client_ix = clients
.iter() .iter()
@ -243,6 +239,10 @@ async fn test_random_collaboration(
operation_channels[client_ix].unbounded_send(()).unwrap(); operation_channels[client_ix].unbounded_send(()).unwrap();
i += 1; i += 1;
} }
if quiesce {
deterministic.run_until_parked();
}
} }
} }
} }
@ -444,12 +444,20 @@ struct UserTestPlan {
#[derive(Debug)] #[derive(Debug)]
enum Operation { enum Operation {
AddConnection { user_id: UserId }, AddConnection {
RemoveConnection { user_id: UserId }, user_id: UserId,
BounceConnection { user_id: UserId }, },
RemoveConnection {
user_id: UserId,
},
BounceConnection {
user_id: UserId,
},
RestartServer, RestartServer,
RunUntilParked, MutateClients {
MutateClients(Vec<UserId>), user_ids: Vec<UserId>,
quiesce: bool,
},
} }
#[derive(Debug)] #[derive(Debug)]
@ -490,7 +498,7 @@ impl TestPlan {
async fn next_operation(&mut self, clients: &[(Rc<TestClient>, TestAppContext)]) -> Operation { async fn next_operation(&mut self, clients: &[(Rc<TestClient>, TestAppContext)]) -> Operation {
let operation = loop { let operation = loop {
break match self.rng.gen_range(0..100) { break match self.rng.gen_range(0..100) {
0..=19 if clients.len() < self.users.len() => { 0..=29 if clients.len() < self.users.len() => {
let user = self let user = self
.users .users
.iter() .iter()
@ -501,20 +509,19 @@ impl TestPlan {
user_id: user.user_id, user_id: user.user_id,
} }
} }
20..=24 if clients.len() > 1 && self.allow_client_disconnection => { 30..=34 if clients.len() > 1 && self.allow_client_disconnection => {
let (client, cx) = &clients[self.rng.gen_range(0..clients.len())]; let (client, cx) = &clients[self.rng.gen_range(0..clients.len())];
let user_id = client.current_user_id(cx); let user_id = client.current_user_id(cx);
Operation::RemoveConnection { user_id } Operation::RemoveConnection { user_id }
} }
25..=29 if clients.len() > 1 && self.allow_client_reconnection => { 35..=39 if clients.len() > 1 && self.allow_client_reconnection => {
let (client, cx) = &clients[self.rng.gen_range(0..clients.len())]; let (client, cx) = &clients[self.rng.gen_range(0..clients.len())];
let user_id = client.current_user_id(cx); let user_id = client.current_user_id(cx);
Operation::BounceConnection { user_id } Operation::BounceConnection { user_id }
} }
30..=34 if self.allow_server_restarts && clients.len() > 1 => { 40..=44 if self.allow_server_restarts && clients.len() > 1 => {
Operation::RestartServer Operation::RestartServer
} }
35..=39 => Operation::RunUntilParked,
_ if !clients.is_empty() => { _ if !clients.is_empty() => {
let user_ids = (0..self.rng.gen_range(0..10)) let user_ids = (0..self.rng.gen_range(0..10))
.map(|_| { .map(|_| {
@ -523,7 +530,10 @@ impl TestPlan {
client.current_user_id(cx) client.current_user_id(cx)
}) })
.collect(); .collect();
Operation::MutateClients(user_ids) Operation::MutateClients {
user_ids,
quiesce: self.rng.gen(),
}
} }
_ => continue, _ => continue,
}; };
@ -541,78 +551,95 @@ impl TestPlan {
let operation = loop { let operation = loop {
match self.rng.gen_range(0..100) { match self.rng.gen_range(0..100) {
// Mutate the call // Mutate the call
0..=19 => match self.rng.gen_range(0..100_u32) { 0..=29 => {
// Respond to an incoming call // Respond to an incoming call
0..=39 => { if call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
if call.read_with(cx, |call, _| call.incoming().borrow().is_some()) { break if self.rng.gen_bool(0.7) {
break if self.rng.gen_bool(0.7) { ClientOperation::AcceptIncomingCall
ClientOperation::AcceptIncomingCall } else {
} else { ClientOperation::RejectIncomingCall
ClientOperation::RejectIncomingCall };
};
}
} }
// Invite a contact to the current call match self.rng.gen_range(0..100_u32) {
30..=89 => { // Invite a contact to the current call
let available_contacts = 0..=70 => {
client.user_store.read_with(cx, |user_store, _| { let available_contacts =
user_store client.user_store.read_with(cx, |user_store, _| {
.contacts() user_store
.iter() .contacts()
.filter(|contact| contact.online && !contact.busy) .iter()
.cloned() .filter(|contact| contact.online && !contact.busy)
.collect::<Vec<_>>() .cloned()
}); .collect::<Vec<_>>()
if !available_contacts.is_empty() { });
let contact = available_contacts.choose(&mut self.rng).unwrap(); if !available_contacts.is_empty() {
break ClientOperation::InviteContactToCall { let contact = available_contacts.choose(&mut self.rng).unwrap();
user_id: UserId(contact.user.id as i32), break ClientOperation::InviteContactToCall {
}; user_id: UserId(contact.user.id as i32),
};
}
} }
}
// Leave the current call // Leave the current call
90.. => { 71.. => {
if self.allow_client_disconnection if self.allow_client_disconnection
&& call.read_with(cx, |call, _| call.room().is_some()) && call.read_with(cx, |call, _| call.room().is_some())
{ {
break ClientOperation::LeaveCall; break ClientOperation::LeaveCall;
}
} }
} }
}, }
// Mutate projects // Mutate projects
20..=39 => match self.rng.gen_range(0..100_u32) { 39..=59 => match self.rng.gen_range(0..100_u32) {
// Open a remote project // Open a new project
0..=30 => { 0..=70 => {
// Open a remote project
if let Some(room) = call.read_with(cx, |call, _| call.room().cloned()) { if let Some(room) = call.read_with(cx, |call, _| call.room().cloned()) {
let remote_projects = room.read_with(cx, |room, _| { let existing_remote_project_ids = cx.read(|cx| {
client
.remote_projects()
.iter()
.map(|p| p.read(cx).remote_id().unwrap())
.collect::<Vec<_>>()
});
let new_remote_projects = room.read_with(cx, |room, _| {
room.remote_participants() room.remote_participants()
.values() .values()
.flat_map(|participant| { .flat_map(|participant| {
participant.projects.iter().map(|project| { participant.projects.iter().filter_map(|project| {
( if existing_remote_project_ids.contains(&project.id) {
UserId::from_proto(participant.user.id), None
project.worktree_root_names[0].clone(), } else {
) Some((
UserId::from_proto(participant.user.id),
project.worktree_root_names[0].clone(),
))
}
}) })
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
}); });
if !remote_projects.is_empty() { if !new_remote_projects.is_empty() {
let (host_id, first_root_name) = let (host_id, first_root_name) =
remote_projects.choose(&mut self.rng).unwrap().clone(); new_remote_projects.choose(&mut self.rng).unwrap().clone();
break ClientOperation::OpenRemoteProject { break ClientOperation::OpenRemoteProject {
host_id, host_id,
first_root_name, first_root_name,
}; };
} }
} }
// Open a local project
else {
let first_root_name = self.next_root_dir_name(user_id);
break ClientOperation::OpenLocalProject { first_root_name };
}
} }
// Close a remote project // Close a remote project
31..=40 => { 71..=80 => {
if !client.remote_projects().is_empty() { if !client.remote_projects().is_empty() {
let project = client let project = client
.remote_projects() .remote_projects()
@ -626,14 +653,8 @@ impl TestPlan {
} }
} }
// Open a local project
41..=60 => {
let first_root_name = self.next_root_dir_name(user_id);
break ClientOperation::OpenLocalProject { first_root_name };
}
// Add a worktree to a local project // Add a worktree to a local project
61.. => { 81.. => {
if !client.local_projects().is_empty() { if !client.local_projects().is_empty() {
let project = client let project = client
.local_projects() .local_projects()
@ -659,7 +680,7 @@ impl TestPlan {
}, },
// Mutate buffers // Mutate buffers
40..=79 => { 60.. => {
let Some(project) = choose_random_project(client, &mut self.rng) else { continue }; let Some(project) = choose_random_project(client, &mut self.rng) else { continue };
let project_root_name = root_name_for_project(&project, cx); let project_root_name = root_name_for_project(&project, cx);
@ -871,9 +892,8 @@ async fn simulate_client(
let operation = plan.lock().next_client_operation(&client, &cx).await; let operation = plan.lock().next_client_operation(&client, &cx).await;
if let Err(error) = apply_client_operation(&client, plan.clone(), operation, &mut cx).await if let Err(error) = apply_client_operation(&client, plan.clone(), operation, &mut cx).await
{ {
log::error!("{} error: {:?}", client.username, error); log::error!("{} error: {}", client.username, error);
} }
cx.background().simulate_random_delay().await; cx.background().simulate_random_delay().await;
} }
log::info!("{}: done", client.username); log::info!("{}: done", client.username);
@ -928,34 +948,7 @@ async fn apply_client_operation(
.await .await
.unwrap(); .unwrap();
let project = client.build_local_project(root_path, cx).await.0; let project = client.build_local_project(root_path, cx).await.0;
ensure_project_shared(&project, client, cx).await;
let active_call = cx.read(ActiveCall::global);
if active_call.read_with(cx, |call, _| call.room().is_some())
&& project.read_with(cx, |project, _| project.is_local() && !project.is_shared())
{
match active_call
.update(cx, |call, cx| call.share_project(project.clone(), cx))
.await
{
Ok(project_id) => {
log::info!(
"{}: shared project {} with id {}",
client.username,
first_root_name,
project_id
);
}
Err(error) => {
log::error!(
"{}: error sharing project {}: {:?}",
client.username,
first_root_name,
error
);
}
}
}
client.local_projects_mut().push(project.clone()); client.local_projects_mut().push(project.clone());
} }
@ -971,6 +964,7 @@ async fn apply_client_operation(
); );
let project = project_for_root_name(client, &project_root_name, cx) let project = project_for_root_name(client, &project_root_name, cx)
.expect("invalid project in test operation"); .expect("invalid project in test operation");
ensure_project_shared(&project, client, cx).await;
if !client.fs.paths().await.contains(&new_root_path) { if !client.fs.paths().await.contains(&new_root_path) {
client.fs.create_dir(&new_root_path).await.unwrap(); client.fs.create_dir(&new_root_path).await.unwrap();
} }
@ -984,13 +978,13 @@ async fn apply_client_operation(
ClientOperation::CloseRemoteProject { project_root_name } => { ClientOperation::CloseRemoteProject { project_root_name } => {
log::info!( log::info!(
"{}: dropping project with root path {}", "{}: closing remote project with root path {}",
client.username, client.username,
project_root_name, project_root_name,
); );
let ix = project_ix_for_root_name(&*client.remote_projects(), &project_root_name, cx) let ix = project_ix_for_root_name(&*client.remote_projects(), &project_root_name, cx)
.expect("invalid project in test operation"); .expect("invalid project in test operation");
client.remote_projects_mut().remove(ix); cx.update(|_| client.remote_projects_mut().remove(ix));
} }
ClientOperation::OpenRemoteProject { ClientOperation::OpenRemoteProject {
@ -1027,13 +1021,14 @@ async fn apply_client_operation(
full_path, full_path,
} => { } => {
log::info!( log::info!(
"{}: opening path {:?} in project {}", "{}: opening buffer {:?} in project {}",
client.username, client.username,
full_path, full_path,
project_root_name, project_root_name,
); );
let project = project_for_root_name(client, &project_root_name, cx) let project = project_for_root_name(client, &project_root_name, cx)
.expect("invalid project in test operation"); .expect("invalid project in test operation");
// ensure_project_shared(&project, client, cx).await;
let mut components = full_path.components(); let mut components = full_path.components();
let root_name = components.next().unwrap().as_os_str().to_str().unwrap(); let root_name = components.next().unwrap().as_os_str().to_str().unwrap();
let path = components.as_path(); let path = components.as_path();
@ -1086,10 +1081,10 @@ async fn apply_client_operation(
}); });
} }
_ => { ClientOperation::Other => {
let choice = plan.lock().rng.gen_range(0..100); let choice = plan.lock().rng.gen_range(0..100);
match choice { match choice {
50..=59 0..=59
if !client.local_projects().is_empty() if !client.local_projects().is_empty()
|| !client.remote_projects().is_empty() => || !client.remote_projects().is_empty() =>
{ {
@ -1147,6 +1142,40 @@ fn root_name_for_project(project: &ModelHandle<Project>, cx: &TestAppContext) ->
}) })
} }
async fn ensure_project_shared(
project: &ModelHandle<Project>,
client: &TestClient,
cx: &mut TestAppContext,
) {
let first_root_name = root_name_for_project(project, cx);
let active_call = cx.read(ActiveCall::global);
if active_call.read_with(cx, |call, _| call.room().is_some())
&& project.read_with(cx, |project, _| project.is_local() && !project.is_shared())
{
match active_call
.update(cx, |call, cx| call.share_project(project.clone(), cx))
.await
{
Ok(project_id) => {
log::info!(
"{}: shared project {} with id {}",
client.username,
first_root_name,
project_id
);
}
Err(error) => {
log::error!(
"{}: error sharing project {}: {:?}",
client.username,
first_root_name,
error
);
}
}
}
}
async fn randomly_mutate_fs(client: &TestClient, plan: &Arc<Mutex<TestPlan>>) { async fn randomly_mutate_fs(client: &TestClient, plan: &Arc<Mutex<TestPlan>>) {
let is_dir = plan.lock().rng.gen::<bool>(); let is_dir = plan.lock().rng.gen::<bool>();
let mut new_path = client let mut new_path = client