Extract a scheduler crate from GPUI to enable unified integration testing of client and server code (#37326)

Extracts and cleans up GPUI's scheduler code into a new `scheduler`
crate, making it pluggable by external runtimes. This will enable
deterministic integration testing with cloud components by providing a
unified test scheduler across Zed and backend code. In Zed, it will
replace the existing GPUI scheduler for consistent async task management
across platforms.

## Changes

- **Core Implementation**: `TestScheduler` with seed-based
randomization, session tracking (`SessionId`), and foreground/background
task separation for reproducible testing.
- **Executors**: `ForegroundExecutor` (!Send, thread-local) and
`BackgroundExecutor` (Send, with blocking/timeout support) as
GPUI-compatible wrappers.
- **Clock and Timer**: Controllable `TestClock` and future-based `Timer`
for time-sensitive tests.
- **Testing APIs**: `once()`, `with_seed()`, and `many()` methods for
configurable test runs.
- **Dependencies**: Added `async-task`, `chrono`, `futures`, etc., with
updates to `Cargo.toml` and lock file.

## Benefits

- **Integration Testing**: Facilitates reliable async tests involving
cloud sessions, reducing flakiness via deterministic execution.
- **Pluggability**: Trait-based design (`Scheduler`) allows easy
integration into non-GPUI runtimes while maintaining GPUI compatibility.
- **Cleanup**: Refactors GPUI scheduler logic for clarity, correctness
(no `unwrap()`, proper error handling), and extensibility.

Follows Rust guidelines; run `./script/clippy` for verification.

- [x] Define and test a core scheduler that we think can power our cloud
code and GPUI
- [ ] Replace GPUI's scheduler


Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Nathan Sobo
2025-09-04 17:14:53 +02:00
committed by GitHub
co-authored by Antonio Scandurra
parent a05f86f97b
commit 1ae326432e
64 changed files with 1569 additions and 473 deletions
+13
View File
@@ -12,6 +12,19 @@
- Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead
* When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback.
* Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`.
* When creating new crates, prefer specifying the library root path in `Cargo.toml` using `[lib] path = "...rs"` instead of the default `lib.rs`, to maintain consistent and descriptive naming (e.g., `gpui.rs` or `main.rs`).
* Avoid creative additions unless explicitly requested
* Use full words for variable names (no abbreviations like "q" for "queue")
* Use variable shadowing to scope clones in async contexts for clarity, minimizing the lifetime of borrowed references.
Example:
```rust
executor.spawn({
let task_ran = task_ran.clone();
async move {
*task_ran.borrow_mut() = true;
}
});
```
# GPUI
Generated
+43 -30
View File
@@ -26,7 +26,7 @@ dependencies = [
"portable-pty",
"project",
"prompt_store",
"rand 0.8.5",
"rand 0.9.1",
"serde",
"serde_json",
"settings",
@@ -79,7 +79,7 @@ dependencies = [
"log",
"pretty_assertions",
"project",
"rand 0.8.5",
"rand 0.9.1",
"serde_json",
"settings",
"text",
@@ -172,7 +172,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
"rand 0.8.5",
"rand 0.9.1",
"ref-cast",
"rope",
"schemars",
@@ -408,7 +408,7 @@ dependencies = [
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"rand 0.9.1",
"release_channel",
"rope",
"rules_library",
@@ -834,7 +834,7 @@ dependencies = [
"project",
"prompt_store",
"proto",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"rpc",
"serde",
@@ -933,7 +933,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"serde",
"serde_json",
@@ -985,7 +985,7 @@ dependencies = [
"pretty_assertions",
"project",
"prompt_store",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"reqwest_client",
"rust-embed",
@@ -2478,7 +2478,7 @@ dependencies = [
"language",
"log",
"pretty_assertions",
"rand 0.8.5",
"rand 0.9.1",
"rope",
"serde_json",
"sum_tree",
@@ -2899,7 +2899,7 @@ dependencies = [
"language",
"log",
"postage",
"rand 0.8.5",
"rand 0.9.1",
"release_channel",
"rpc",
"settings",
@@ -3086,7 +3086,7 @@ dependencies = [
"parking_lot",
"paths",
"postage",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"release_channel",
"rpc",
@@ -3335,7 +3335,7 @@ dependencies = [
"prometheus",
"prompt_store",
"prost 0.9.0",
"rand 0.8.5",
"rand 0.9.1",
"recent_projects",
"release_channel",
"remote",
@@ -4697,7 +4697,7 @@ dependencies = [
"markdown",
"pretty_assertions",
"project",
"rand 0.8.5",
"rand 0.9.1",
"serde",
"serde_json",
"settings",
@@ -5068,7 +5068,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"release_channel",
"rpc",
@@ -5563,7 +5563,7 @@ dependencies = [
"parking_lot",
"paths",
"project",
"rand 0.8.5",
"rand 0.9.1",
"release_channel",
"remote",
"reqwest_client",
@@ -6412,7 +6412,7 @@ dependencies = [
"log",
"parking_lot",
"pretty_assertions",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"rope",
"schemars",
@@ -7465,7 +7465,7 @@ dependencies = [
"pathfinder_geometry",
"postage",
"profiling",
"rand 0.8.5",
"rand 0.9.1",
"raw-window-handle",
"refineable",
"reqwest_client",
@@ -9078,7 +9078,7 @@ dependencies = [
"parking_lot",
"postage",
"pretty_assertions",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"rpc",
"schemars",
@@ -10392,7 +10392,7 @@ dependencies = [
"parking_lot",
"pretty_assertions",
"project",
"rand 0.8.5",
"rand 0.9.1",
"rope",
"serde",
"settings",
@@ -12618,7 +12618,7 @@ dependencies = [
"postage",
"prettier",
"pretty_assertions",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"release_channel",
"remote",
@@ -13892,7 +13892,7 @@ dependencies = [
"ctor",
"gpui",
"log",
"rand 0.8.5",
"rand 0.9.1",
"rayon",
"smallvec",
"sum_tree",
@@ -13921,7 +13921,7 @@ dependencies = [
"gpui",
"parking_lot",
"proto",
"rand 0.8.5",
"rand 0.9.1",
"rsa",
"serde",
"serde_json",
@@ -14356,6 +14356,19 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "scheduler"
version = "0.1.0"
dependencies = [
"async-task",
"chrono",
"futures 0.3.31",
"parking",
"parking_lot",
"rand 0.9.1",
"workspace-hack",
]
[[package]]
name = "schema_generator"
version = "0.1.0"
@@ -15655,7 +15668,7 @@ name = "streaming_diff"
version = "0.1.0"
dependencies = [
"ordered-float 2.10.1",
"rand 0.8.5",
"rand 0.9.1",
"rope",
"util",
"workspace-hack",
@@ -15769,7 +15782,7 @@ dependencies = [
"arrayvec",
"ctor",
"log",
"rand 0.8.5",
"rand 0.9.1",
"rayon",
"workspace-hack",
"zlog",
@@ -16360,7 +16373,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"libc",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"release_channel",
"schemars",
@@ -16408,7 +16421,7 @@ dependencies = [
"language",
"log",
"project",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"schemars",
"search",
@@ -16440,7 +16453,7 @@ dependencies = [
"log",
"parking_lot",
"postage",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"rope",
"smallvec",
@@ -17797,7 +17810,7 @@ dependencies = [
"libc",
"log",
"nix 0.29.0",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"rust-embed",
"schemars",
@@ -18588,7 +18601,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"parking_lot",
"rand 0.8.5",
"rand 0.9.1",
"workspace-hack",
"zlog",
]
@@ -20047,7 +20060,7 @@ dependencies = [
"paths",
"postage",
"pretty_assertions",
"rand 0.8.5",
"rand 0.9.1",
"rpc",
"schemars",
"serde",
@@ -20812,7 +20825,7 @@ dependencies = [
"menu",
"postage",
"project",
"rand 0.8.5",
"rand 0.9.1",
"regex",
"release_channel",
"reqwest_client",
+5 -1
View File
@@ -131,6 +131,7 @@ members = [
"crates/refineable",
"crates/refineable/derive_refineable",
"crates/release_channel",
"crates/scheduler",
"crates/remote",
"crates/remote_server",
"crates/repl",
@@ -360,6 +361,7 @@ proto = { path = "crates/proto" }
recent_projects = { path = "crates/recent_projects" }
refineable = { path = "crates/refineable" }
release_channel = { path = "crates/release_channel" }
scheduler = { path = "crates/scheduler" }
remote = { path = "crates/remote" }
remote_server = { path = "crates/remote_server" }
repl = { path = "crates/repl" }
@@ -444,6 +446,7 @@ async-fs = "2.1"
async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
async-recursion = "1.0.0"
async-tar = "0.5.0"
async-task = "4.7"
async-trait = "0.1"
async-tungstenite = "0.29.1"
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
@@ -538,6 +541,7 @@ objc = "0.2"
open = "5.0.0"
ordered-float = "2.1.1"
palette = { version = "0.7.5", default-features = false, features = ["std"] }
parking = "2.0"
parking_lot = "0.12.1"
partial-json-fixer = "0.5.3"
parse_int = "0.9"
@@ -560,7 +564,7 @@ prost-build = "0.9"
prost-types = "0.9"
pulldown-cmark = { version = "0.12.0", default-features = false }
quote = "1.0.9"
rand = "0.8.5"
rand = "0.9"
rayon = "1.8"
ref-cast = "1.0.24"
regex = "1.5"
+3 -3
View File
@@ -2114,7 +2114,7 @@ mod tests {
use gpui::{App, AsyncApp, TestAppContext, WeakEntity};
use indoc::indoc;
use project::{FakeFs, Fs};
use rand::Rng as _;
use rand::{distr, prelude::*};
use serde_json::json;
use settings::SettingsStore;
use smol::stream::StreamExt as _;
@@ -3057,8 +3057,8 @@ mod tests {
cx: &mut App,
) -> Task<gpui::Result<Entity<AcpThread>>> {
let session_id = acp::SessionId(
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
rand::rng()
.sample_iter(&distr::Alphanumeric)
.take(7)
.map(char::from)
.collect::<String>()
+3 -3
View File
@@ -2218,7 +2218,7 @@ mod tests {
action_log.update(cx, |log, cx| log.buffer_read(buffer.clone(), cx));
for _ in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..25 => {
action_log.update(cx, |log, cx| {
let range = buffer.read(cx).random_byte_range(0, &mut rng);
@@ -2237,7 +2237,7 @@ mod tests {
.unwrap();
}
_ => {
let is_agent_edit = rng.gen_bool(0.5);
let is_agent_edit = rng.random_bool(0.5);
if is_agent_edit {
log::info!("agent edit");
} else {
@@ -2252,7 +2252,7 @@ mod tests {
}
}
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
quiesce(&action_log, &buffer, cx);
}
}
+3 -3
View File
@@ -1139,7 +1139,7 @@ mod tests {
);
while !new_text.is_empty() {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let len = rng.random_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
@@ -1208,7 +1208,7 @@ mod tests {
);
while !new_text.is_empty() {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let len = rng.random_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
@@ -1277,7 +1277,7 @@ mod tests {
);
while !new_text.is_empty() {
let max_len = cmp::min(new_text.len(), 10);
let len = rng.gen_range(1..=max_len);
let len = rng.random_range(1..=max_len);
let (chunk, suffix) = new_text.split_at(len);
chunks_tx.unbounded_send(chunk.to_string()).unwrap();
new_text = suffix;
@@ -764,7 +764,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
let network = Arc::new(Mutex::new(Network::new(rng.clone())));
let mut contexts = Vec::new();
let num_peers = rng.gen_range(min_peers..=max_peers);
let num_peers = rng.random_range(min_peers..=max_peers);
let context_id = ContextId::new();
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
for i in 0..num_peers {
@@ -806,10 +806,10 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
|| !network.lock().is_idle()
|| network.lock().contains_disconnected_peers()
{
let context_index = rng.gen_range(0..contexts.len());
let context_index = rng.random_range(0..contexts.len());
let context = &contexts[context_index];
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..=29 if mutation_count > 0 => {
log::info!("Context {}: edit buffer", context_index);
context.update(cx, |context, cx| {
@@ -874,10 +874,10 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
merge_same_roles: true,
})];
let num_sections = rng.gen_range(0..=3);
let num_sections = rng.random_range(0..=3);
let mut section_start = 0;
for _ in 0..num_sections {
let mut section_end = rng.gen_range(section_start..=output_text.len());
let mut section_end = rng.random_range(section_start..=output_text.len());
while !output_text.is_char_boundary(section_end) {
section_end += 1;
}
@@ -924,7 +924,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
75..=84 if mutation_count > 0 => {
context.update(cx, |context, cx| {
if let Some(message) = context.messages(cx).choose(&mut rng) {
let new_status = match rng.gen_range(0..3) {
let new_status = match rng.random_range(0..3) {
0 => MessageStatus::Done,
1 => MessageStatus::Pending,
_ => MessageStatus::Error(SharedString::from("Random error")),
@@ -971,7 +971,7 @@ async fn test_random_context_collaboration(cx: &mut TestAppContext, mut rng: Std
network.lock().broadcast(replica_id, ops_to_send);
context.update(cx, |context, cx| context.apply_ops(ops_to_receive, cx));
} else if rng.gen_bool(0.1) && replica_id != 0 {
} else if rng.random_bool(0.1) && replica_id != 0 {
log::info!("Context {}: disconnecting", context_index);
network.lock().disconnect_peer(replica_id);
} else if network.lock().has_unreceived(replica_id) {
+4 -4
View File
@@ -1315,17 +1315,17 @@ mod tests {
#[gpui::test(iterations = 100)]
async fn test_random_indents(mut rng: StdRng) {
let len = rng.gen_range(1..=100);
let len = rng.random_range(1..=100);
let new_text = util::RandomCharIter::new(&mut rng)
.with_simple_text()
.take(len)
.collect::<String>();
let new_text = new_text
.split('\n')
.map(|line| format!("{}{}", " ".repeat(rng.gen_range(0..=8)), line))
.map(|line| format!("{}{}", " ".repeat(rng.random_range(0..=8)), line))
.collect::<Vec<_>>()
.join("\n");
let delta = IndentDelta::Spaces(rng.gen_range(-4..=4));
let delta = IndentDelta::Spaces(rng.random_range(-4i8..=4i8) as isize);
let chunks = to_random_chunks(&mut rng, &new_text);
let new_text_chunks = stream::iter(chunks.iter().enumerate().map(|(index, chunk)| {
@@ -1357,7 +1357,7 @@ mod tests {
}
fn to_random_chunks(rng: &mut StdRng, input: &str) -> Vec<String> {
let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
chunk_indices.sort();
chunk_indices.push(input.len());
@@ -204,7 +204,7 @@ mod tests {
}
fn parse_random_chunks(input: &str, parser: &mut CreateFileParser, rng: &mut StdRng) -> String {
let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
chunk_indices.sort();
chunk_indices.push(input.len());
@@ -996,7 +996,7 @@ mod tests {
}
fn parse_random_chunks(input: &str, parser: &mut EditParser, rng: &mut StdRng) -> Vec<Edit> {
let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
chunk_indices.sort();
chunk_indices.push(input.len());
@@ -1399,7 +1399,7 @@ fn eval(
}
fn run_eval(eval: EvalInput, tx: mpsc::Sender<Result<EvalOutput>>) {
let dispatcher = gpui::TestDispatcher::new(StdRng::from_entropy());
let dispatcher = gpui::TestDispatcher::new(StdRng::from_os_rng());
let mut cx = TestAppContext::build(dispatcher, None);
let output = cx.executor().block_test(async {
let test = EditAgentTest::new(&mut cx).await;
@@ -1707,7 +1707,7 @@ async fn retry_on_rate_limit<R>(mut request: impl AsyncFnMut() -> Result<R>) ->
};
if let Some(retry_after) = retry_delay {
let jitter = retry_after.mul_f64(rand::thread_rng().gen_range(0.0..1.0));
let jitter = retry_after.mul_f64(rand::rng().random_range(0.0..1.0));
eprintln!("Attempt #{attempt}: Retry after {retry_after:?} + jitter of {jitter:?}");
Timer::after(retry_after + jitter).await;
} else {
@@ -771,7 +771,7 @@ mod tests {
}
fn to_random_chunks(rng: &mut StdRng, input: &str) -> Vec<String> {
let chunk_count = rng.gen_range(1..=cmp::min(input.len(), 50));
let chunk_count = rng.random_range(1..=cmp::min(input.len(), 50));
let mut chunk_indices = (0..input.len()).choose_multiple(rng, chunk_count);
chunk_indices.sort();
chunk_indices.push(input.len());
+9 -8
View File
@@ -2044,10 +2044,10 @@ mod tests {
#[gpui::test(iterations = 100)]
async fn test_staging_and_unstaging_hunks(cx: &mut TestAppContext, mut rng: StdRng) {
fn gen_line(rng: &mut StdRng) -> String {
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
"\n".to_owned()
} else {
let c = rng.gen_range('A'..='Z');
let c = rng.random_range('A'..='Z');
format!("{c}{c}{c}\n")
}
}
@@ -2066,7 +2066,7 @@ mod tests {
old_lines.into_iter()
};
let mut result = String::new();
let unchanged_count = rng.gen_range(0..=old_lines.len());
let unchanged_count = rng.random_range(0..=old_lines.len());
result +=
&old_lines
.by_ref()
@@ -2076,14 +2076,14 @@ mod tests {
s
});
while old_lines.len() > 0 {
let deleted_count = rng.gen_range(0..=old_lines.len());
let deleted_count = rng.random_range(0..=old_lines.len());
let _advance = old_lines
.by_ref()
.take(deleted_count)
.map(|line| line.len() + 1)
.sum::<usize>();
let minimum_added = if deleted_count == 0 { 1 } else { 0 };
let added_count = rng.gen_range(minimum_added..=5);
let added_count = rng.random_range(minimum_added..=5);
let addition = (0..added_count).map(|_| gen_line(rng)).collect::<String>();
result += &addition;
@@ -2092,7 +2092,8 @@ mod tests {
if blank_lines == old_lines.len() {
break;
};
let unchanged_count = rng.gen_range((blank_lines + 1).max(1)..=old_lines.len());
let unchanged_count =
rng.random_range((blank_lines + 1).max(1)..=old_lines.len());
result += &old_lines.by_ref().take(unchanged_count).fold(
String::new(),
|mut s, line| {
@@ -2149,7 +2150,7 @@ mod tests {
)
});
let working_copy = working_copy.read_with(cx, |working_copy, _| working_copy.snapshot());
let mut index_text = if rng.r#gen() {
let mut index_text = if rng.random() {
Rope::from(head_text.as_str())
} else {
working_copy.as_rope().clone()
@@ -2165,7 +2166,7 @@ mod tests {
}
for _ in 0..operations {
let i = rng.gen_range(0..hunks.len());
let i = rng.random_range(0..hunks.len());
let hunk = &mut hunks[i];
let hunk_to_change = hunk.clone();
let stage = match hunk.secondary_status {
+3 -3
View File
@@ -129,7 +129,7 @@ impl ChannelChat {
loaded_all_messages: false,
next_pending_message_id: 0,
last_acknowledged_id: None,
rng: StdRng::from_entropy(),
rng: StdRng::from_os_rng(),
first_loaded_message_id: None,
_subscription: subscription.set_entity(&cx.entity(), &cx.to_async()),
}
@@ -183,7 +183,7 @@ impl ChannelChat {
let channel_id = self.channel_id;
let pending_id = ChannelMessageId::Pending(post_inc(&mut self.next_pending_message_id));
let nonce = self.rng.r#gen();
let nonce = self.rng.random();
self.insert_messages(
SumTree::from_item(
ChannelMessage {
@@ -257,7 +257,7 @@ impl ChannelChat {
cx,
);
let nonce: u128 = self.rng.r#gen();
let nonce: u128 = self.rng.random();
let request = self.rpc.request(proto::UpdateChannelMessage {
channel_id: self.channel_id.0,
+4 -3
View File
@@ -691,7 +691,7 @@ impl Client {
#[cfg(any(test, feature = "test-support"))]
let mut rng = StdRng::seed_from_u64(0);
#[cfg(not(any(test, feature = "test-support")))]
let mut rng = StdRng::from_entropy();
let mut rng = StdRng::from_os_rng();
let mut delay = INITIAL_RECONNECTION_DELAY;
loop {
@@ -721,8 +721,9 @@ impl Client {
},
cx,
);
let jitter =
Duration::from_millis(rng.gen_range(0..delay.as_millis() as u64));
let jitter = Duration::from_millis(
rng.random_range(0..delay.as_millis() as u64),
);
cx.background_executor().timer(delay + jitter).await;
delay = cmp::min(delay * 2, MAX_RECONNECTION_DELAY);
} else {
+35 -2
View File
@@ -227,7 +227,7 @@ pub async fn verify_access_token(
#[cfg(test)]
mod test {
use rand::thread_rng;
use rand::prelude::*;
use scrypt::password_hash::{PasswordHasher, SaltString};
use sea_orm::EntityTrait;
@@ -358,9 +358,42 @@ mod test {
None,
None,
params,
&SaltString::generate(thread_rng()),
&SaltString::generate(PasswordHashRngCompat::new()),
)
.map_err(anyhow::Error::new)?
.to_string())
}
// TODO: remove once we password_hash v0.6 is released.
struct PasswordHashRngCompat(rand::rngs::ThreadRng);
impl PasswordHashRngCompat {
fn new() -> Self {
Self(rand::rng())
}
}
impl scrypt::password_hash::rand_core::RngCore for PasswordHashRngCompat {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest);
}
fn try_fill_bytes(
&mut self,
dest: &mut [u8],
) -> Result<(), scrypt::password_hash::rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl scrypt::password_hash::rand_core::CryptoRng for PasswordHashRngCompat {}
}
+1 -1
View File
@@ -256,7 +256,7 @@ impl Database {
let test_options = self.test_options.as_ref().unwrap();
test_options.executor.simulate_random_delay().await;
let fail_probability = *test_options.query_failure_probability.lock();
if test_options.executor.rng().gen_bool(fail_probability) {
if test_options.executor.rng().random_bool(fail_probability) {
return Err(anyhow!("simulated query failure"))?;
}
+2 -2
View File
@@ -75,10 +75,10 @@ impl TestDb {
static LOCK: Mutex<()> = Mutex::new(());
let _guard = LOCK.lock();
let mut rng = StdRng::from_entropy();
let mut rng = StdRng::from_os_rng();
let url = format!(
"postgres://postgres@localhost/zed-test-{}",
rng.r#gen::<u128>()
rng.random::<u128>()
);
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
+1 -1
View File
@@ -5746,7 +5746,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
let definitions;
let buffer_b2;
if rng.r#gen() {
if rng.random() {
cx_a.run_until_parked();
cx_b.run_until_parked();
definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
@@ -84,7 +84,7 @@ impl RandomizedTest for RandomChannelBufferTest {
}
loop {
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
0..=29 => {
let channel_name = client.channel_store().read_with(cx, |store, cx| {
store.ordered_channels().find_map(|(_, channel)| {
@@ -17,7 +17,7 @@ use project::{
DEFAULT_COMPLETION_CONTEXT, Project, ProjectPath, search::SearchQuery, search::SearchResult,
};
use rand::{
distributions::{Alphanumeric, DistString},
distr::{self, SampleString},
prelude::*,
};
use serde::{Deserialize, Serialize};
@@ -168,19 +168,19 @@ impl RandomizedTest for ProjectCollaborationTest {
) -> ClientOperation {
let call = cx.read(ActiveCall::global);
loop {
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
// Mutate the call
0..=29 => {
// Respond to an incoming call
if call.read_with(cx, |call, _| call.incoming().borrow().is_some()) {
break if rng.gen_bool(0.7) {
break if rng.random_bool(0.7) {
ClientOperation::AcceptIncomingCall
} else {
ClientOperation::RejectIncomingCall
};
}
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
// Invite a contact to the current call
0..=70 => {
let available_contacts =
@@ -212,7 +212,7 @@ impl RandomizedTest for ProjectCollaborationTest {
}
// Mutate projects
30..=59 => match rng.gen_range(0..100_u32) {
30..=59 => match rng.random_range(0..100_u32) {
// Open a new project
0..=70 => {
// Open a remote project
@@ -270,7 +270,7 @@ impl RandomizedTest for ProjectCollaborationTest {
}
// Mutate project worktrees
81.. => match rng.gen_range(0..100_u32) {
81.. => match rng.random_range(0..100_u32) {
// Add a worktree to a local project
0..=50 => {
let Some(project) = client.local_projects().choose(rng).cloned() else {
@@ -279,7 +279,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let project_root_name = root_name_for_project(&project, cx);
let mut paths = client.fs().paths(false);
paths.remove(0);
let new_root_path = if paths.is_empty() || rng.r#gen() {
let new_root_path = if paths.is_empty() || rng.random() {
Path::new(path!("/")).join(plan.next_root_dir_name())
} else {
paths.choose(rng).unwrap().clone()
@@ -309,7 +309,7 @@ impl RandomizedTest for ProjectCollaborationTest {
.choose(rng)
});
let Some(worktree) = worktree else { continue };
let is_dir = rng.r#gen::<bool>();
let is_dir = rng.random::<bool>();
let mut full_path =
worktree.read_with(cx, |w, _| PathBuf::from(w.root_name()));
full_path.push(gen_file_name(rng));
@@ -334,7 +334,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let project_root_name = root_name_for_project(&project, cx);
let is_local = project.read_with(cx, |project, _| project.is_local());
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
// Manipulate an existing buffer
0..=70 => {
let Some(buffer) = client
@@ -349,7 +349,7 @@ impl RandomizedTest for ProjectCollaborationTest {
let full_path = buffer
.read_with(cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
// Close the buffer
0..=15 => {
break ClientOperation::CloseBuffer {
@@ -360,7 +360,7 @@ impl RandomizedTest for ProjectCollaborationTest {
}
// Save the buffer
16..=29 if buffer.read_with(cx, |b, _| b.is_dirty()) => {
let detach = rng.gen_bool(0.3);
let detach = rng.random_bool(0.3);
break ClientOperation::SaveBuffer {
project_root_name,
is_local,
@@ -383,17 +383,17 @@ impl RandomizedTest for ProjectCollaborationTest {
_ => {
let offset = buffer.read_with(cx, |buffer, _| {
buffer.clip_offset(
rng.gen_range(0..=buffer.len()),
rng.random_range(0..=buffer.len()),
language::Bias::Left,
)
});
let detach = rng.r#gen();
let detach = rng.random();
break ClientOperation::RequestLspDataInBuffer {
project_root_name,
full_path,
offset,
is_local,
kind: match rng.gen_range(0..5_u32) {
kind: match rng.random_range(0..5_u32) {
0 => LspRequestKind::Rename,
1 => LspRequestKind::Highlights,
2 => LspRequestKind::Definition,
@@ -407,8 +407,8 @@ impl RandomizedTest for ProjectCollaborationTest {
}
71..=80 => {
let query = rng.gen_range('a'..='z').to_string();
let detach = rng.gen_bool(0.3);
let query = rng.random_range('a'..='z').to_string();
let detach = rng.random_bool(0.3);
break ClientOperation::SearchProject {
project_root_name,
is_local,
@@ -460,7 +460,7 @@ impl RandomizedTest for ProjectCollaborationTest {
// Create or update a file or directory
96.. => {
let is_dir = rng.r#gen::<bool>();
let is_dir = rng.random::<bool>();
let content;
let mut path;
let dir_paths = client.fs().directories(false);
@@ -470,11 +470,11 @@ impl RandomizedTest for ProjectCollaborationTest {
path = dir_paths.choose(rng).unwrap().clone();
path.push(gen_file_name(rng));
} else {
content = Alphanumeric.sample_string(rng, 16);
content = distr::Alphanumeric.sample_string(rng, 16);
// Create a new file or overwrite an existing file
let file_paths = client.fs().files();
if file_paths.is_empty() || rng.gen_bool(0.5) {
if file_paths.is_empty() || rng.random_bool(0.5) {
path = dir_paths.choose(rng).unwrap().clone();
path.push(gen_file_name(rng));
path.set_extension("rs");
@@ -1090,7 +1090,7 @@ impl RandomizedTest for ProjectCollaborationTest {
move |_, cx| {
let background = cx.background_executor();
let mut rng = background.rng();
let count = rng.gen_range::<usize, _>(1..3);
let count = rng.random_range::<usize, _>(1..3);
let files = fs.as_fake().files();
let files = (0..count)
.map(|_| files.choose(&mut rng).unwrap().clone())
@@ -1117,12 +1117,12 @@ impl RandomizedTest for ProjectCollaborationTest {
let background = cx.background_executor();
let mut rng = background.rng();
let highlight_count = rng.gen_range(1..=5);
let highlight_count = rng.random_range(1..=5);
for _ in 0..highlight_count {
let start_row = rng.gen_range(0..100);
let start_column = rng.gen_range(0..100);
let end_row = rng.gen_range(0..100);
let end_column = rng.gen_range(0..100);
let start_row = rng.random_range(0..100);
let start_column = rng.random_range(0..100);
let end_row = rng.random_range(0..100);
let end_column = rng.random_range(0..100);
let start = PointUtf16::new(start_row, start_column);
let end = PointUtf16::new(end_row, end_column);
let range =
@@ -1219,8 +1219,8 @@ impl RandomizedTest for ProjectCollaborationTest {
guest_project.remote_id(),
);
assert_eq!(
guest_snapshot.entries(false, 0).collect::<Vec<_>>(),
host_snapshot.entries(false, 0).collect::<Vec<_>>(),
guest_snapshot.entries(false, 0).map(null_out_entry_size).collect::<Vec<_>>(),
host_snapshot.entries(false, 0).map(null_out_entry_size).collect::<Vec<_>>(),
"{} has different snapshot than the host for worktree {:?} ({:?}) and project {:?}",
client.username,
host_snapshot.abs_path(),
@@ -1248,6 +1248,18 @@ impl RandomizedTest for ProjectCollaborationTest {
);
}
});
// A hack to work around a hack in
// https://github.com/zed-industries/zed/pull/16696 that wasn't
// detected until we upgraded the rng crate. This whole crate is
// going away with DeltaDB soon, so we hold our nose and
// continue.
fn null_out_entry_size(entry: &project::Entry) -> project::Entry {
project::Entry {
size: 0,
..entry.clone()
}
}
}
let buffers = client.buffers().clone();
@@ -1422,7 +1434,7 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
.filter(|path| path.starts_with(repo_path))
.collect::<Vec<_>>();
let count = rng.gen_range(0..=paths.len());
let count = rng.random_range(0..=paths.len());
paths.shuffle(rng);
paths.truncate(count);
@@ -1434,13 +1446,13 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
let repo_path = client.fs().directories(false).choose(rng).unwrap().clone();
match rng.gen_range(0..100_u32) {
match rng.random_range(0..100_u32) {
0..=25 => {
let file_paths = generate_file_paths(&repo_path, rng, client);
let contents = file_paths
.into_iter()
.map(|path| (path, Alphanumeric.sample_string(rng, 16)))
.map(|path| (path, distr::Alphanumeric.sample_string(rng, 16)))
.collect();
GitOperation::WriteGitIndex {
@@ -1449,7 +1461,8 @@ fn generate_git_operation(rng: &mut StdRng, client: &TestClient) -> GitOperation
}
}
26..=63 => {
let new_branch = (rng.gen_range(0..10) > 3).then(|| Alphanumeric.sample_string(rng, 8));
let new_branch =
(rng.random_range(0..10) > 3).then(|| distr::Alphanumeric.sample_string(rng, 8));
GitOperation::WriteGitBranch {
repo_path,
@@ -1596,7 +1609,7 @@ fn choose_random_project(client: &TestClient, rng: &mut StdRng) -> Option<Entity
fn gen_file_name(rng: &mut StdRng) -> String {
let mut name = String::new();
for _ in 0..10 {
let letter = rng.gen_range('a'..='z');
let letter = rng.random_range('a'..='z');
name.push(letter);
}
name
@@ -1604,7 +1617,7 @@ fn gen_file_name(rng: &mut StdRng) -> String {
fn gen_status(rng: &mut StdRng) -> FileStatus {
fn gen_tracked_status(rng: &mut StdRng) -> TrackedStatus {
match rng.gen_range(0..3) {
match rng.random_range(0..3) {
0 => TrackedStatus {
index_status: StatusCode::Unmodified,
worktree_status: StatusCode::Unmodified,
@@ -1626,7 +1639,7 @@ fn gen_status(rng: &mut StdRng) -> FileStatus {
}
fn gen_unmerged_status_code(rng: &mut StdRng) -> UnmergedStatusCode {
match rng.gen_range(0..3) {
match rng.random_range(0..3) {
0 => UnmergedStatusCode::Updated,
1 => UnmergedStatusCode::Added,
2 => UnmergedStatusCode::Deleted,
@@ -1634,7 +1647,7 @@ fn gen_status(rng: &mut StdRng) -> FileStatus {
}
}
match rng.gen_range(0..2) {
match rng.random_range(0..2) {
0 => FileStatus::Unmerged(UnmergedStatus {
first_head: gen_unmerged_status_code(rng),
second_head: gen_unmerged_status_code(rng),
@@ -208,9 +208,9 @@ pub fn save_randomized_test_plan() {
impl<T: RandomizedTest> TestPlan<T> {
pub async fn new(server: &mut TestServer, mut rng: StdRng) -> Arc<Mutex<Self>> {
let allow_server_restarts = rng.gen_bool(0.7);
let allow_client_reconnection = rng.gen_bool(0.7);
let allow_client_disconnection = rng.gen_bool(0.1);
let allow_server_restarts = rng.random_bool(0.7);
let allow_client_reconnection = rng.random_bool(0.7);
let allow_client_disconnection = rng.random_bool(0.1);
let mut users = Vec::new();
for ix in 0..max_peers() {
@@ -407,7 +407,7 @@ impl<T: RandomizedTest> TestPlan<T> {
}
Some(loop {
break match self.rng.gen_range(0..100) {
break match self.rng.random_range(0..100) {
0..=29 if clients.len() < self.users.len() => {
let user = self
.users
@@ -421,13 +421,13 @@ impl<T: RandomizedTest> TestPlan<T> {
}
}
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.random_range(0..clients.len())];
let user_id = client.current_user_id(cx);
self.operation_ix += 1;
ServerOperation::RemoveConnection { user_id }
}
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.random_range(0..clients.len())];
let user_id = client.current_user_id(cx);
self.operation_ix += 1;
ServerOperation::BounceConnection { user_id }
@@ -439,12 +439,12 @@ impl<T: RandomizedTest> TestPlan<T> {
_ if !clients.is_empty() => {
let count = self
.rng
.gen_range(1..10)
.random_range(1..10)
.min(self.max_operations - self.operation_ix);
let batch_id = util::post_inc(&mut self.next_batch_id);
let mut user_ids = (0..count)
.map(|_| {
let ix = self.rng.gen_range(0..clients.len());
let ix = self.rng.random_range(0..clients.len());
let (client, cx) = &clients[ix];
client.current_user_id(cx)
})
@@ -453,7 +453,7 @@ impl<T: RandomizedTest> TestPlan<T> {
ServerOperation::MutateClients {
user_ids,
batch_id,
quiesce: self.rng.gen_bool(0.7),
quiesce: self.rng.random_bool(0.7),
}
}
_ => continue,
+23 -22
View File
@@ -682,7 +682,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
Default::default();
for _ in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
// language server completes its diagnostic check
0..=20 if !updated_language_servers.is_empty() => {
let server_id = *updated_language_servers.iter().choose(&mut rng).unwrap();
@@ -691,7 +691,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
lsp_store.disk_based_diagnostics_finished(server_id, cx)
});
if rng.gen_bool(0.5) {
if rng.random_bool(0.5) {
cx.run_until_parked();
}
}
@@ -701,7 +701,7 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
let (path, server_id, diagnostics) =
match current_diagnostics.iter_mut().choose(&mut rng) {
// update existing set of diagnostics
Some(((path, server_id), diagnostics)) if rng.gen_bool(0.5) => {
Some(((path, server_id), diagnostics)) if rng.random_bool(0.5) => {
(path.clone(), *server_id, diagnostics)
}
@@ -709,13 +709,13 @@ async fn test_random_diagnostics_blocks(cx: &mut TestAppContext, mut rng: StdRng
_ => {
let path: PathBuf =
format!(path!("/test/{}.rs"), post_inc(&mut next_filename)).into();
let len = rng.gen_range(128..256);
let len = rng.random_range(128..256);
let content =
RandomCharIter::new(&mut rng).take(len).collect::<String>();
fs.insert_file(&path, content.into_bytes()).await;
let server_id = match language_server_ids.iter().choose(&mut rng) {
Some(server_id) if rng.gen_bool(0.5) => *server_id,
Some(server_id) if rng.random_bool(0.5) => *server_id,
_ => {
let id = LanguageServerId(language_server_ids.len());
language_server_ids.push(id);
@@ -846,7 +846,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
let mut next_inlay_id = 0;
for _ in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
// language server completes its diagnostic check
0..=20 if !updated_language_servers.is_empty() => {
let server_id = *updated_language_servers.iter().choose(&mut rng).unwrap();
@@ -855,7 +855,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
lsp_store.disk_based_diagnostics_finished(server_id, cx)
});
if rng.gen_bool(0.5) {
if rng.random_bool(0.5) {
cx.run_until_parked();
}
}
@@ -864,7 +864,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
diagnostics.editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(window, cx);
if !snapshot.buffer_snapshot.is_empty() {
let position = rng.gen_range(0..snapshot.buffer_snapshot.len());
let position = rng.random_range(0..snapshot.buffer_snapshot.len());
let position = snapshot.buffer_snapshot.clip_offset(position, Bias::Left);
log::info!(
"adding inlay at {position}/{}: {:?}",
@@ -890,7 +890,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
let (path, server_id, diagnostics) =
match current_diagnostics.iter_mut().choose(&mut rng) {
// update existing set of diagnostics
Some(((path, server_id), diagnostics)) if rng.gen_bool(0.5) => {
Some(((path, server_id), diagnostics)) if rng.random_bool(0.5) => {
(path.clone(), *server_id, diagnostics)
}
@@ -898,13 +898,13 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
_ => {
let path: PathBuf =
format!(path!("/test/{}.rs"), post_inc(&mut next_filename)).into();
let len = rng.gen_range(128..256);
let len = rng.random_range(128..256);
let content =
RandomCharIter::new(&mut rng).take(len).collect::<String>();
fs.insert_file(&path, content.into_bytes()).await;
let server_id = match language_server_ids.iter().choose(&mut rng) {
Some(server_id) if rng.gen_bool(0.5) => *server_id,
Some(server_id) if rng.random_bool(0.5) => *server_id,
_ => {
let id = LanguageServerId(language_server_ids.len());
language_server_ids.push(id);
@@ -1589,10 +1589,10 @@ fn randomly_update_diagnostics_for_path(
next_id: &mut usize,
rng: &mut impl Rng,
) {
let mutation_count = rng.gen_range(1..=3);
let mutation_count = rng.random_range(1..=3);
for _ in 0..mutation_count {
if rng.gen_bool(0.3) && !diagnostics.is_empty() {
let idx = rng.gen_range(0..diagnostics.len());
if rng.random_bool(0.3) && !diagnostics.is_empty() {
let idx = rng.random_range(0..diagnostics.len());
log::info!(" removing diagnostic at index {idx}");
diagnostics.remove(idx);
} else {
@@ -1601,7 +1601,7 @@ fn randomly_update_diagnostics_for_path(
let new_diagnostic = random_lsp_diagnostic(rng, fs, path, unique_id);
let ix = rng.gen_range(0..=diagnostics.len());
let ix = rng.random_range(0..=diagnostics.len());
log::info!(
" inserting {} at index {ix}. {},{}..{},{}",
new_diagnostic.message,
@@ -1638,8 +1638,8 @@ fn random_lsp_diagnostic(
let file_content = fs.read_file_sync(path).unwrap();
let file_text = Rope::from(String::from_utf8_lossy(&file_content).as_ref());
let start = rng.gen_range(0..file_text.len().saturating_add(ERROR_MARGIN));
let end = rng.gen_range(start..file_text.len().saturating_add(ERROR_MARGIN));
let start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
let end = rng.random_range(start..file_text.len().saturating_add(ERROR_MARGIN));
let start_point = file_text.offset_to_point_utf16(start);
let end_point = file_text.offset_to_point_utf16(end);
@@ -1649,7 +1649,7 @@ fn random_lsp_diagnostic(
lsp::Position::new(end_point.row, end_point.column),
);
let severity = if rng.gen_bool(0.5) {
let severity = if rng.random_bool(0.5) {
Some(lsp::DiagnosticSeverity::ERROR)
} else {
Some(lsp::DiagnosticSeverity::WARNING)
@@ -1657,13 +1657,14 @@ fn random_lsp_diagnostic(
let message = format!("diagnostic {unique_id}");
let related_information = if rng.gen_bool(0.3) {
let info_count = rng.gen_range(1..=3);
let related_information = if rng.random_bool(0.3) {
let info_count = rng.random_range(1..=3);
let mut related_info = Vec::with_capacity(info_count);
for i in 0..info_count {
let info_start = rng.gen_range(0..file_text.len().saturating_add(ERROR_MARGIN));
let info_end = rng.gen_range(info_start..file_text.len().saturating_add(ERROR_MARGIN));
let info_start = rng.random_range(0..file_text.len().saturating_add(ERROR_MARGIN));
let info_end =
rng.random_range(info_start..file_text.len().saturating_add(ERROR_MARGIN));
let info_start_point = file_text.offset_to_point_utf16(info_start);
let info_end_point = file_text.offset_to_point_utf16(info_end);
+29 -30
View File
@@ -1552,15 +1552,15 @@ pub mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let mut tab_size = rng.gen_range(1..=4);
let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
let excerpt_header_height = rng.gen_range(1..=5);
let mut tab_size = rng.random_range(1..=4);
let buffer_start_excerpt_header_height = rng.random_range(1..=5);
let excerpt_header_height = rng.random_range(1..=5);
let font_size = px(14.0);
let max_wrap_width = 300.0;
let mut wrap_width = if rng.gen_bool(0.1) {
let mut wrap_width = if rng.random_bool(0.1) {
None
} else {
Some(px(rng.gen_range(0.0..=max_wrap_width)))
Some(px(rng.random_range(0.0..=max_wrap_width)))
};
log::info!("tab size: {}", tab_size);
@@ -1571,8 +1571,8 @@ pub mod tests {
});
let buffer = cx.update(|cx| {
if rng.r#gen() {
let len = rng.gen_range(0..10);
if rng.random() {
let len = rng.random_range(0..10);
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();
@@ -1609,12 +1609,12 @@ pub mod tests {
log::info!("display text: {:?}", snapshot.text());
for _i in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..=19 => {
wrap_width = if rng.gen_bool(0.2) {
wrap_width = if rng.random_bool(0.2) {
None
} else {
Some(px(rng.gen_range(0.0..=max_wrap_width)))
Some(px(rng.random_range(0.0..=max_wrap_width)))
};
log::info!("setting wrap width to {:?}", wrap_width);
map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1634,28 +1634,27 @@ pub mod tests {
}
30..=44 => {
map.update(cx, |map, cx| {
if rng.r#gen() || blocks.is_empty() {
if rng.random() || blocks.is_empty() {
let buffer = map.snapshot(cx).buffer_snapshot;
let block_properties = (0..rng.gen_range(1..=1))
let block_properties = (0..rng.random_range(1..=1))
.map(|_| {
let position =
buffer.anchor_after(buffer.clip_offset(
rng.gen_range(0..=buffer.len()),
Bias::Left,
));
let position = buffer.anchor_after(buffer.clip_offset(
rng.random_range(0..=buffer.len()),
Bias::Left,
));
let placement = if rng.r#gen() {
let placement = if rng.random() {
BlockPlacement::Above(position)
} else {
BlockPlacement::Below(position)
};
let height = rng.gen_range(1..5);
let height = rng.random_range(1..5);
log::info!(
"inserting block {:?} with height {}",
placement.as_ref().map(|p| p.to_point(&buffer)),
height
);
let priority = rng.gen_range(1..100);
let priority = rng.random_range(1..100);
BlockProperties {
placement,
style: BlockStyle::Fixed,
@@ -1668,9 +1667,9 @@ pub mod tests {
blocks.extend(map.insert_blocks(block_properties, cx));
} else {
blocks.shuffle(&mut rng);
let remove_count = rng.gen_range(1..=4.min(blocks.len()));
let remove_count = rng.random_range(1..=4.min(blocks.len()));
let block_ids_to_remove = (0..remove_count)
.map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
.map(|_| blocks.remove(rng.random_range(0..blocks.len())))
.collect();
log::info!("removing block ids {:?}", block_ids_to_remove);
map.remove_blocks(block_ids_to_remove, cx);
@@ -1679,16 +1678,16 @@ pub mod tests {
}
45..=79 => {
let mut ranges = Vec::new();
for _ in 0..rng.gen_range(1..=3) {
for _ in 0..rng.random_range(1..=3) {
buffer.read_with(cx, |buffer, cx| {
let buffer = buffer.read(cx);
let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
ranges.push(start..end);
});
}
if rng.r#gen() && fold_count > 0 {
if rng.random() && fold_count > 0 {
log::info!("unfolding ranges: {:?}", ranges);
map.update(cx, |map, cx| {
map.unfold_intersecting(ranges, true, cx);
@@ -1727,8 +1726,8 @@ pub mod tests {
// Line boundaries
let buffer = &snapshot.buffer_snapshot;
for _ in 0..5 {
let row = rng.gen_range(0..=buffer.max_point().row);
let column = rng.gen_range(0..=buffer.line_len(MultiBufferRow(row)));
let row = rng.random_range(0..=buffer.max_point().row);
let column = rng.random_range(0..=buffer.line_len(MultiBufferRow(row)));
let point = buffer.clip_point(Point::new(row, column), Left);
let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
@@ -1776,8 +1775,8 @@ pub mod tests {
let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
let max_point = snapshot.clip_point(snapshot.max_point(), Right);
for _ in 0..5 {
let row = rng.gen_range(0..=snapshot.max_point().row().0);
let column = rng.gen_range(0..=snapshot.line_len(DisplayRow(row)));
let row = rng.random_range(0..=snapshot.max_point().row().0);
let column = rng.random_range(0..=snapshot.line_len(DisplayRow(row)));
let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
log::info!("Moving from point {:?}", point);
+28 -28
View File
@@ -128,10 +128,10 @@ impl<T> BlockPlacement<T> {
}
}
fn sort_order(&self) -> u8 {
fn tie_break(&self) -> u8 {
match self {
BlockPlacement::Above(_) => 0,
BlockPlacement::Replace(_) => 1,
BlockPlacement::Replace(_) => 0,
BlockPlacement::Above(_) => 1,
BlockPlacement::Near(_) => 2,
BlockPlacement::Below(_) => 3,
}
@@ -143,7 +143,7 @@ impl BlockPlacement<Anchor> {
self.start()
.cmp(other.start(), buffer)
.then_with(|| other.end().cmp(self.end(), buffer))
.then_with(|| self.sort_order().cmp(&other.sort_order()))
.then_with(|| self.tie_break().cmp(&other.tie_break()))
}
fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
@@ -847,6 +847,7 @@ impl BlockMap {
.start()
.cmp(placement_b.start())
.then_with(|| placement_b.end().cmp(placement_a.end()))
.then_with(|| placement_a.tie_break().cmp(&placement_b.tie_break()))
.then_with(|| {
if block_a.is_header() {
Ordering::Less
@@ -856,7 +857,6 @@ impl BlockMap {
Ordering::Equal
}
})
.then_with(|| placement_a.sort_order().cmp(&placement_b.sort_order()))
.then_with(|| match (block_a, block_b) {
(
Block::ExcerptBoundary {
@@ -2922,21 +2922,21 @@ mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let wrap_width = if rng.gen_bool(0.2) {
let wrap_width = if rng.random_bool(0.2) {
None
} else {
Some(px(rng.gen_range(0.0..=100.0)))
Some(px(rng.random_range(0.0..=100.0)))
};
let tab_size = 1.try_into().unwrap();
let font_size = px(14.0);
let buffer_start_header_height = rng.gen_range(1..=5);
let excerpt_header_height = rng.gen_range(1..=5);
let buffer_start_header_height = rng.random_range(1..=5);
let excerpt_header_height = rng.random_range(1..=5);
log::info!("Wrap width: {:?}", wrap_width);
log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
let is_singleton = rng.r#gen();
let is_singleton = rng.random();
let buffer = if is_singleton {
let len = rng.gen_range(0..10);
let len = rng.random_range(0..10);
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
log::info!("initial singleton buffer text: {:?}", text);
cx.update(|cx| MultiBuffer::build_simple(&text, cx))
@@ -2966,30 +2966,30 @@ mod tests {
for _ in 0..operations {
let mut buffer_edits = Vec::new();
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=19 => {
let wrap_width = if rng.gen_bool(0.2) {
let wrap_width = if rng.random_bool(0.2) {
None
} else {
Some(px(rng.gen_range(0.0..=100.0)))
Some(px(rng.random_range(0.0..=100.0)))
};
log::info!("Setting wrap width to {:?}", wrap_width);
wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
}
20..=39 => {
let block_count = rng.gen_range(1..=5);
let block_count = rng.random_range(1..=5);
let block_properties = (0..block_count)
.map(|_| {
let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
let offset =
buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
let mut min_height = 0;
let placement = match rng.gen_range(0..3) {
let placement = match rng.random_range(0..3) {
0 => {
min_height = 1;
let start = buffer.anchor_after(offset);
let end = buffer.anchor_after(buffer.clip_offset(
rng.gen_range(offset..=buffer.len()),
rng.random_range(offset..=buffer.len()),
Bias::Left,
));
BlockPlacement::Replace(start..=end)
@@ -2998,7 +2998,7 @@ mod tests {
_ => BlockPlacement::Below(buffer.anchor_after(offset)),
};
let height = rng.gen_range(min_height..5);
let height = rng.random_range(min_height..5);
BlockProperties {
style: BlockStyle::Fixed,
placement,
@@ -3040,7 +3040,7 @@ mod tests {
}
}
40..=59 if !block_map.custom_blocks.is_empty() => {
let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
let block_ids_to_remove = block_map
.custom_blocks
.choose_multiple(&mut rng, block_count)
@@ -3095,8 +3095,8 @@ mod tests {
let mut folded_count = folded_buffers.len();
let mut unfolded_count = unfolded_buffers.len();
let fold = !unfolded_buffers.is_empty() && rng.gen_bool(0.5);
let unfold = !folded_buffers.is_empty() && rng.gen_bool(0.5);
let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
if !fold && !unfold {
log::info!(
"Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
@@ -3107,7 +3107,7 @@ mod tests {
buffer.update(cx, |buffer, cx| {
if fold {
let buffer_to_fold =
unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
log::info!("Folding {buffer_to_fold:?}");
let related_excerpts = buffer_snapshot
.excerpts()
@@ -3133,7 +3133,7 @@ mod tests {
}
if unfold {
let buffer_to_unfold =
folded_buffers[rng.gen_range(0..folded_buffers.len())];
folded_buffers[rng.random_range(0..folded_buffers.len())];
log::info!("Unfolding {buffer_to_unfold:?}");
unfolded_count += 1;
folded_count -= 1;
@@ -3146,7 +3146,7 @@ mod tests {
}
_ => {
buffer.update(cx, |buffer, cx| {
let mutation_count = rng.gen_range(1..=5);
let mutation_count = rng.random_range(1..=5);
let subscription = buffer.subscribe();
buffer.randomly_mutate(&mut rng, mutation_count, cx);
buffer_snapshot = buffer.snapshot(cx);
@@ -3331,7 +3331,7 @@ mod tests {
);
for start_row in 0..expected_row_count {
let end_row = rng.gen_range(start_row + 1..=expected_row_count);
let end_row = rng.random_range(start_row + 1..=expected_row_count);
let mut expected_text = expected_lines[start_row..end_row].join("\n");
if end_row < expected_row_count {
expected_text.push('\n');
@@ -3426,8 +3426,8 @@ mod tests {
);
for _ in 0..10 {
let end_row = rng.gen_range(1..=expected_lines.len());
let start_row = rng.gen_range(0..end_row);
let end_row = rng.random_range(1..=expected_lines.len());
let start_row = rng.random_range(0..end_row);
let mut expected_longest_rows_in_range = vec![];
let mut longest_line_len_in_range = 0;
+26 -22
View File
@@ -1771,9 +1771,9 @@ mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let len = rng.gen_range(0..10);
let len = rng.random_range(0..10);
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
let buffer = if rng.r#gen() {
let buffer = if rng.random() {
MultiBuffer::build_simple(&text, cx)
} else {
MultiBuffer::build_random(&mut rng, cx)
@@ -1790,7 +1790,7 @@ mod tests {
log::info!("text: {:?}", buffer_snapshot.text());
let mut buffer_edits = Vec::new();
let mut inlay_edits = Vec::new();
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=39 => {
snapshot_edits.extend(map.randomly_mutate(&mut rng));
}
@@ -1800,7 +1800,7 @@ mod tests {
}
_ => buffer.update(cx, |buffer, cx| {
let subscription = buffer.subscribe();
let edit_count = rng.gen_range(1..=5);
let edit_count = rng.random_range(1..=5);
buffer.randomly_mutate(&mut rng, edit_count, cx);
buffer_snapshot = buffer.snapshot(cx);
let edits = subscription.consume().into_inner();
@@ -1917,10 +1917,14 @@ mod tests {
}
for _ in 0..5 {
let mut start = snapshot
.clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Left);
let mut end = snapshot
.clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Right);
let mut start = snapshot.clip_offset(
FoldOffset(rng.random_range(0..=snapshot.len().0)),
Bias::Left,
);
let mut end = snapshot.clip_offset(
FoldOffset(rng.random_range(0..=snapshot.len().0)),
Bias::Right,
);
if start > end {
mem::swap(&mut start, &mut end);
}
@@ -1975,8 +1979,8 @@ mod tests {
for _ in 0..5 {
let end =
buffer_snapshot.clip_offset(rng.gen_range(0..=buffer_snapshot.len()), Right);
let start = buffer_snapshot.clip_offset(rng.gen_range(0..=end), Left);
buffer_snapshot.clip_offset(rng.random_range(0..=buffer_snapshot.len()), Right);
let start = buffer_snapshot.clip_offset(rng.random_range(0..=end), Left);
let expected_folds = map
.snapshot
.folds
@@ -2001,10 +2005,10 @@ mod tests {
let text = snapshot.text();
for _ in 0..5 {
let start_row = rng.gen_range(0..=snapshot.max_point().row());
let start_column = rng.gen_range(0..=snapshot.line_len(start_row));
let end_row = rng.gen_range(0..=snapshot.max_point().row());
let end_column = rng.gen_range(0..=snapshot.line_len(end_row));
let start_row = rng.random_range(0..=snapshot.max_point().row());
let start_column = rng.random_range(0..=snapshot.line_len(start_row));
let end_row = rng.random_range(0..=snapshot.max_point().row());
let end_column = rng.random_range(0..=snapshot.line_len(end_row));
let mut start =
snapshot.clip_point(FoldPoint::new(start_row, start_column), Bias::Left);
let mut end = snapshot.clip_point(FoldPoint::new(end_row, end_column), Bias::Right);
@@ -2109,17 +2113,17 @@ mod tests {
rng: &mut impl Rng,
) -> Vec<(FoldSnapshot, Vec<FoldEdit>)> {
let mut snapshot_edits = Vec::new();
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=39 if !self.snapshot.folds.is_empty() => {
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let mut to_unfold = Vec::new();
for _ in 0..rng.gen_range(1..=3) {
let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
for _ in 0..rng.random_range(1..=3) {
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
to_unfold.push(start..end);
}
let inclusive = rng.r#gen();
let inclusive = rng.random();
log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
snapshot_edits.push((snapshot, edits));
@@ -2130,9 +2134,9 @@ mod tests {
let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
let buffer = &inlay_snapshot.buffer;
let mut to_fold = Vec::new();
for _ in 0..rng.gen_range(1..=2) {
let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
for _ in 0..rng.random_range(1..=2) {
let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right);
let start = buffer.clip_offset(rng.random_range(0..=end), Left);
to_fold.push((start..end, FoldPlaceholder::test()));
}
log::info!("folding {:?}", to_fold);
+20 -16
View File
@@ -719,14 +719,18 @@ impl InlayMap {
let mut to_remove = Vec::new();
let mut to_insert = Vec::new();
let snapshot = &mut self.snapshot;
for i in 0..rng.gen_range(1..=5) {
if self.inlays.is_empty() || rng.r#gen() {
for i in 0..rng.random_range(1..=5) {
if self.inlays.is_empty() || rng.random() {
let position = snapshot.buffer.random_byte_range(0, rng).start;
let bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
let len = if rng.gen_bool(0.01) {
let bias = if rng.random() {
Bias::Left
} else {
Bias::Right
};
let len = if rng.random_bool(0.01) {
0
} else {
rng.gen_range(1..=5)
rng.random_range(1..=5)
};
let text = util::RandomCharIter::new(&mut *rng)
.filter(|ch| *ch != '\r')
@@ -1665,8 +1669,8 @@ mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let len = rng.gen_range(0..30);
let buffer = if rng.r#gen() {
let len = rng.random_range(0..30);
let buffer = if rng.random() {
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();
@@ -1683,7 +1687,7 @@ mod tests {
let mut prev_inlay_text = inlay_snapshot.text();
let mut buffer_edits = Vec::new();
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=50 => {
let (snapshot, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
log::info!("mutated text: {:?}", snapshot.text());
@@ -1691,7 +1695,7 @@ mod tests {
}
_ => buffer.update(cx, |buffer, cx| {
let subscription = buffer.subscribe();
let edit_count = rng.gen_range(1..=5);
let edit_count = rng.random_range(1..=5);
buffer.randomly_mutate(&mut rng, edit_count, cx);
buffer_snapshot = buffer.snapshot(cx);
let edits = subscription.consume().into_inner();
@@ -1740,7 +1744,7 @@ mod tests {
}
let mut text_highlights = TextHighlights::default();
let text_highlight_count = rng.gen_range(0_usize..10);
let text_highlight_count = rng.random_range(0_usize..10);
let mut text_highlight_ranges = (0..text_highlight_count)
.map(|_| buffer_snapshot.random_byte_range(0, &mut rng))
.collect::<Vec<_>>();
@@ -1762,10 +1766,10 @@ mod tests {
let mut inlay_highlights = InlayHighlights::default();
if !inlays.is_empty() {
let inlay_highlight_count = rng.gen_range(0..inlays.len());
let inlay_highlight_count = rng.random_range(0..inlays.len());
let mut inlay_indices = BTreeSet::default();
while inlay_indices.len() < inlay_highlight_count {
inlay_indices.insert(rng.gen_range(0..inlays.len()));
inlay_indices.insert(rng.random_range(0..inlays.len()));
}
let new_highlights = TreeMap::from_ordered_entries(
inlay_indices
@@ -1782,8 +1786,8 @@ mod tests {
}),
n => {
let inlay_text = inlay.text.to_string();
let mut highlight_end = rng.gen_range(1..n);
let mut highlight_start = rng.gen_range(0..highlight_end);
let mut highlight_end = rng.random_range(1..n);
let mut highlight_start = rng.random_range(0..highlight_end);
while !inlay_text.is_char_boundary(highlight_end) {
highlight_end += 1;
}
@@ -1805,9 +1809,9 @@ mod tests {
}
for _ in 0..5 {
let mut end = rng.gen_range(0..=inlay_snapshot.len().0);
let mut end = rng.random_range(0..=inlay_snapshot.len().0);
end = expected_text.clip_offset(end, Bias::Right);
let mut start = rng.gen_range(0..=end);
let mut start = rng.random_range(0..=end);
start = expected_text.clip_offset(start, Bias::Right);
let range = InlayOffset(start)..InlayOffset(end);
+7 -7
View File
@@ -736,9 +736,9 @@ mod tests {
#[gpui::test(iterations = 100)]
fn test_random_tabs(cx: &mut gpui::App, mut rng: StdRng) {
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
let len = rng.gen_range(0..30);
let buffer = if rng.r#gen() {
let tab_size = NonZeroU32::new(rng.random_range(1..=4)).unwrap();
let len = rng.random_range(0..30);
let buffer = if rng.random() {
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();
@@ -769,11 +769,11 @@ mod tests {
);
for _ in 0..5 {
let end_row = rng.gen_range(0..=text.max_point().row);
let end_column = rng.gen_range(0..=text.line_len(end_row));
let end_row = rng.random_range(0..=text.max_point().row);
let end_column = rng.random_range(0..=text.line_len(end_row));
let mut end = TabPoint(text.clip_point(Point::new(end_row, end_column), Bias::Right));
let start_row = rng.gen_range(0..=text.max_point().row);
let start_column = rng.gen_range(0..=text.line_len(start_row));
let start_row = rng.random_range(0..=text.max_point().row);
let start_column = rng.random_range(0..=text.line_len(start_row));
let mut start =
TabPoint(text.clip_point(Point::new(start_row, start_column), Bias::Left));
if start > end {
+12 -12
View File
@@ -1215,12 +1215,12 @@ mod tests {
.unwrap_or(10);
let text_system = cx.read(|cx| cx.text_system().clone());
let mut wrap_width = if rng.gen_bool(0.1) {
let mut wrap_width = if rng.random_bool(0.1) {
None
} else {
Some(px(rng.gen_range(0.0..=1000.0)))
Some(px(rng.random_range(0.0..=1000.0)))
};
let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
let tab_size = NonZeroU32::new(rng.random_range(1..=4)).unwrap();
let font = test_font();
let _font_id = text_system.resolve_font(&font);
@@ -1230,10 +1230,10 @@ mod tests {
log::info!("Wrap width: {:?}", wrap_width);
let buffer = cx.update(|cx| {
if rng.r#gen() {
if rng.random() {
MultiBuffer::build_random(&mut rng, cx)
} else {
let len = rng.gen_range(0..10);
let len = rng.random_range(0..10);
let text = util::RandomCharIter::new(&mut rng)
.take(len)
.collect::<String>();
@@ -1281,12 +1281,12 @@ mod tests {
log::info!("{} ==============================================", _i);
let mut buffer_edits = Vec::new();
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=19 => {
wrap_width = if rng.gen_bool(0.2) {
wrap_width = if rng.random_bool(0.2) {
None
} else {
Some(px(rng.gen_range(0.0..=1000.0)))
Some(px(rng.random_range(0.0..=1000.0)))
};
log::info!("Setting wrap width to {:?}", wrap_width);
wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
@@ -1317,7 +1317,7 @@ mod tests {
_ => {
buffer.update(cx, |buffer, cx| {
let subscription = buffer.subscribe();
let edit_count = rng.gen_range(1..=5);
let edit_count = rng.random_range(1..=5);
buffer.randomly_mutate(&mut rng, edit_count, cx);
buffer_snapshot = buffer.snapshot(cx);
buffer_edits.extend(subscription.consume());
@@ -1341,7 +1341,7 @@ mod tests {
snapshot.verify_chunks(&mut rng);
edits.push((snapshot, wrap_edits));
if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.random_bool(0.4) {
log::info!("Waiting for wrapping to finish");
while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
notifications.next().await.unwrap();
@@ -1479,8 +1479,8 @@ mod tests {
impl WrapSnapshot {
fn verify_chunks(&mut self, rng: &mut impl Rng) {
for _ in 0..5 {
let mut end_row = rng.gen_range(0..=self.max_point().row());
let start_row = rng.gen_range(0..=end_row);
let mut end_row = rng.random_range(0..=self.max_point().row());
let start_row = rng.random_range(0..=end_row);
end_row += 1;
let mut expected_text = self.text_chunks(start_row).collect::<String>();
+2 -2
View File
@@ -164,7 +164,7 @@ use project::{
DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
},
};
use rand::{seq::SliceRandom, thread_rng};
use rand::seq::SliceRandom;
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
use scroll::{Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide};
use selections_collection::{
@@ -10971,7 +10971,7 @@ impl Editor {
}
pub fn shuffle_lines(&mut self, _: &ShuffleLines, window: &mut Window, cx: &mut Context<Self>) {
self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut thread_rng()))
self.manipulate_immutable_lines(window, cx, |lines| lines.shuffle(&mut rand::rng()))
}
fn manipulate_lines<M>(
+4 -4
View File
@@ -1107,7 +1107,7 @@ mod tests {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let buffer_initial_text_len = rng.gen_range(5..15);
let buffer_initial_text_len = rng.random_range(5..15);
let mut buffer_initial_text = Rope::from(
RandomCharIter::new(&mut rng)
.take(buffer_initial_text_len)
@@ -1159,7 +1159,7 @@ mod tests {
git_blame.update(cx, |blame, cx| blame.check_invariants(cx));
for _ in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..=19 => {
log::info!("quiescing");
cx.executor().run_until_parked();
@@ -1202,8 +1202,8 @@ mod tests {
let mut blame_entries = Vec::new();
for ix in 0..5 {
if last_row < max_row {
let row_start = rng.gen_range(last_row..max_row);
let row_end = rng.gen_range(row_start + 1..cmp::min(row_start + 3, max_row) + 1);
let row_start = rng.random_range(last_row..max_row);
let row_end = rng.random_range(row_start + 1..cmp::min(row_start + 3, max_row) + 1);
blame_entries.push(blame_entry(&ix.to_string(), row_start..row_end));
last_row = row_end;
} else {
+27 -27
View File
@@ -38,58 +38,58 @@ pub struct Quote {
impl Quote {
pub fn random() -> Self {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut rng = rand::rng();
// simulate a base price in a realistic range
let prev_close = rng.gen_range(100.0..200.0);
let change = rng.gen_range(-5.0..5.0);
let prev_close = rng.random_range(100.0..200.0);
let change = rng.random_range(-5.0..5.0);
let last_done = prev_close + change;
let open = prev_close + rng.gen_range(-3.0..3.0);
let high = (prev_close + rng.gen_range::<f64, _>(0.0..10.0)).max(open);
let low = (prev_close - rng.gen_range::<f64, _>(0.0..10.0)).min(open);
let timestamp = Duration::from_secs(rng.gen_range(0..86400));
let volume = rng.gen_range(1_000_000..100_000_000);
let open = prev_close + rng.random_range(-3.0..3.0);
let high = (prev_close + rng.random_range::<f64, _>(0.0..10.0)).max(open);
let low = (prev_close - rng.random_range::<f64, _>(0.0..10.0)).min(open);
let timestamp = Duration::from_secs(rng.random_range(0..86400));
let volume = rng.random_range(1_000_000..100_000_000);
let turnover = last_done * volume as f64;
let symbol = {
let mut ticker = String::new();
if rng.gen_bool(0.5) {
if rng.random_bool(0.5) {
ticker.push_str(&format!(
"{:03}.{}",
rng.gen_range(100..1000),
rng.gen_range(0..10)
rng.random_range(100..1000),
rng.random_range(0..10)
));
} else {
ticker.push_str(&format!(
"{}{}",
rng.gen_range('A'..='Z'),
rng.gen_range('A'..='Z')
rng.random_range('A'..='Z'),
rng.random_range('A'..='Z')
));
}
ticker.push_str(&format!(".{}", rng.gen_range('A'..='Z')));
ticker.push_str(&format!(".{}", rng.random_range('A'..='Z')));
ticker
};
let name = format!(
"{} {} - #{}",
symbol,
rng.gen_range(1..100),
rng.gen_range(10000..100000)
rng.random_range(1..100),
rng.random_range(10000..100000)
);
let ttm = rng.gen_range(0.0..10.0);
let market_cap = rng.gen_range(1_000_000.0..10_000_000.0);
let float_cap = market_cap + rng.gen_range(1_000.0..10_000.0);
let shares = rng.gen_range(100.0..1000.0);
let ttm = rng.random_range(0.0..10.0);
let market_cap = rng.random_range(1_000_000.0..10_000_000.0);
let float_cap = market_cap + rng.random_range(1_000.0..10_000.0);
let shares = rng.random_range(100.0..1000.0);
let pb = market_cap / shares;
let pe = market_cap / shares;
let eps = market_cap / shares;
let dividend = rng.gen_range(0.0..10.0);
let dividend_yield = rng.gen_range(0.0..10.0);
let dividend_per_share = rng.gen_range(0.0..10.0);
let dividend = rng.random_range(0.0..10.0);
let dividend_yield = rng.random_range(0.0..10.0);
let dividend_per_share = rng.random_range(0.0..10.0);
let dividend_date = SharedString::new(format!(
"{}-{}-{}",
rng.gen_range(2000..2023),
rng.gen_range(1..12),
rng.gen_range(1..28)
rng.random_range(2000..2023),
rng.random_range(1..12),
rng.random_range(1..28)
));
let dividend_payment = rng.gen_range(0.0..10.0);
let dividend_payment = rng.random_range(0.0..10.0);
Self {
name: name.into(),
+1 -1
View File
@@ -144,7 +144,7 @@ impl TestAppContext {
/// Create a single TestAppContext, for non-multi-client tests
pub fn single() -> Self {
let dispatcher = TestDispatcher::new(StdRng::from_entropy());
let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
Self::build(dispatcher, None)
}
+5 -5
View File
@@ -309,12 +309,12 @@ mod tests {
let mut expected_quads: Vec<(Bounds<f32>, u32)> = Vec::new();
// Insert a random number of random AABBs into the tree.
let num_bounds = rng.gen_range(1..=max_bounds);
let num_bounds = rng.random_range(1..=max_bounds);
for _ in 0..num_bounds {
let min_x: f32 = rng.gen_range(-100.0..100.0);
let min_y: f32 = rng.gen_range(-100.0..100.0);
let width: f32 = rng.gen_range(0.0..50.0);
let height: f32 = rng.gen_range(0.0..50.0);
let min_x: f32 = rng.random_range(-100.0..100.0);
let min_y: f32 = rng.random_range(-100.0..100.0);
let width: f32 = rng.random_range(0.0..50.0);
let height: f32 = rng.random_range(0.0..50.0);
let bounds = Bounds {
origin: Point { x: min_x, y: min_y },
size: Size { width, height },
+5 -5
View File
@@ -118,7 +118,7 @@ impl TestDispatcher {
}
YieldNow {
count: self.state.lock().random.gen_range(0..10),
count: self.state.lock().random.random_range(0..10),
}
}
@@ -151,11 +151,11 @@ impl TestDispatcher {
if deprioritized_background_len == 0 {
return false;
}
let ix = state.random.gen_range(0..deprioritized_background_len);
let ix = state.random.random_range(0..deprioritized_background_len);
main_thread = false;
runnable = state.deprioritized_background.swap_remove(ix);
} else {
main_thread = state.random.gen_ratio(
main_thread = state.random.random_ratio(
foreground_len as u32,
(foreground_len + background_len) as u32,
);
@@ -170,7 +170,7 @@ impl TestDispatcher {
.pop_front()
.unwrap();
} else {
let ix = state.random.gen_range(0..background_len);
let ix = state.random.random_range(0..background_len);
runnable = state.background.swap_remove(ix);
};
};
@@ -241,7 +241,7 @@ impl TestDispatcher {
pub fn gen_block_on_ticks(&self) -> usize {
let mut lock = self.state.lock();
let block_on_ticks = lock.block_on_ticks.clone();
lock.random.gen_range(block_on_ticks)
lock.random.random_range(block_on_ticks)
}
}
+5 -1
View File
@@ -94,7 +94,11 @@ impl WindowsPlatform {
}
let directx_devices = DirectXDevices::new().context("Creating DirectX devices")?;
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
let validation_number = rand::random::<usize>();
let validation_number = if usize::BITS == 64 {
rand::random::<u64>() as usize
} else {
rand::random::<u32>() as usize
};
let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
let text_system = Arc::new(
DirectWriteTextSystem::new(&directx_devices)
+2 -2
View File
@@ -2842,12 +2842,12 @@ impl Buffer {
let new_start = last_end.map_or(0, |last_end| last_end + 1);
let mut range = self.random_byte_range(new_start, rng);
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
mem::swap(&mut range.start, &mut range.end);
}
last_end = Some(range.end);
let new_text_len = rng.gen_range(0..10);
let new_text_len = rng.random_range(0..10);
let mut new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
new_text = new_text.to_uppercase();
+16 -16
View File
@@ -3013,7 +3013,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let base_text_len = rng.gen_range(0..10);
let base_text_len = rng.random_range(0..10);
let base_text = RandomCharIter::new(&mut rng)
.take(base_text_len)
.collect::<String>();
@@ -3022,7 +3022,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
let network = Arc::new(Mutex::new(Network::new(rng.clone())));
let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
for i in 0..rng.gen_range(min_peers..=max_peers) {
for i in 0..rng.random_range(min_peers..=max_peers) {
let buffer = cx.new(|cx| {
let state = base_buffer.read(cx).to_proto(cx);
let ops = cx
@@ -3035,7 +3035,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
.map(|op| proto::deserialize_operation(op).unwrap()),
cx,
);
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
let network = network.clone();
cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
if let BufferEvent::Operation {
@@ -3066,11 +3066,11 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
let mut next_diagnostic_id = 0;
let mut active_selections = BTreeMap::default();
loop {
let replica_index = rng.gen_range(0..replica_ids.len());
let replica_index = rng.random_range(0..replica_ids.len());
let replica_id = replica_ids[replica_index];
let buffer = &mut buffers[replica_index];
let mut new_buffer = None;
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..=29 if mutation_count != 0 => {
buffer.update(cx, |buffer, cx| {
buffer.start_transaction_at(now);
@@ -3082,13 +3082,13 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
}
30..=39 if mutation_count != 0 => {
buffer.update(cx, |buffer, cx| {
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
log::info!("peer {} clearing active selections", replica_id);
active_selections.remove(&replica_id);
buffer.remove_active_selections(cx);
} else {
let mut selections = Vec::new();
for id in 0..rng.gen_range(1..=5) {
for id in 0..rng.random_range(1..=5) {
let range = buffer.random_byte_range(0, &mut rng);
selections.push(Selection {
id,
@@ -3111,7 +3111,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
mutation_count -= 1;
}
40..=49 if mutation_count != 0 && replica_id == 0 => {
let entry_count = rng.gen_range(1..=5);
let entry_count = rng.random_range(1..=5);
buffer.update(cx, |buffer, cx| {
let diagnostics = DiagnosticSet::new(
(0..entry_count).map(|_| {
@@ -3166,7 +3166,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
new_buffer.replica_id(),
new_buffer.text()
);
new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
new_buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
let network = network.clone();
cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
if let BufferEvent::Operation {
@@ -3238,7 +3238,7 @@ fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
_ => {}
}
now += Duration::from_millis(rng.gen_range(0..=200));
now += Duration::from_millis(rng.random_range(0..=200));
buffers.extend(new_buffer);
for buffer in &buffers {
@@ -3320,23 +3320,23 @@ fn test_trailing_whitespace_ranges(mut rng: StdRng) {
// Generate a random multi-line string containing
// some lines with trailing whitespace.
let mut text = String::new();
for _ in 0..rng.gen_range(0..16) {
for _ in 0..rng.gen_range(0..36) {
text.push(match rng.gen_range(0..10) {
for _ in 0..rng.random_range(0..16) {
for _ in 0..rng.random_range(0..36) {
text.push(match rng.random_range(0..10) {
0..=1 => ' ',
3 => '\t',
_ => rng.gen_range('a'..='z'),
_ => rng.random_range('a'..='z'),
});
}
text.push('\n');
}
match rng.gen_range(0..10) {
match rng.random_range(0..10) {
// sometimes remove the last newline
0..=1 => drop(text.pop()), //
// sometimes add extra newlines
2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
_ => {}
}
+20 -19
View File
@@ -3580,7 +3580,7 @@ impl MultiBuffer {
pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
let mutation_count = rng.gen_range(1..=5);
let mutation_count = rng.random_range(1..=5);
multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
multibuffer
})
@@ -3603,16 +3603,17 @@ impl MultiBuffer {
}
let new_start = last_end.map_or(0, |last_end| last_end + 1);
let end = snapshot.clip_offset(rng.gen_range(new_start..=snapshot.len()), Bias::Right);
let start = snapshot.clip_offset(rng.gen_range(new_start..=end), Bias::Right);
let end =
snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right);
let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right);
last_end = Some(end);
let mut range = start..end;
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
mem::swap(&mut range.start, &mut range.end);
}
let new_text_len = rng.gen_range(0..10);
let new_text_len = rng.random_range(0..10);
let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
edits.push((range, new_text.into()));
@@ -3639,18 +3640,18 @@ impl MultiBuffer {
let mut buffers = Vec::new();
for _ in 0..mutation_count {
if rng.gen_bool(0.05) {
if rng.random_bool(0.05) {
log::info!("Clearing multi-buffer");
self.clear(cx);
continue;
} else if rng.gen_bool(0.1) && !self.excerpt_ids().is_empty() {
} else if rng.random_bool(0.1) && !self.excerpt_ids().is_empty() {
let ids = self.excerpt_ids();
let mut excerpts = HashSet::default();
for _ in 0..rng.gen_range(0..ids.len()) {
for _ in 0..rng.random_range(0..ids.len()) {
excerpts.extend(ids.choose(rng).copied());
}
let line_count = rng.gen_range(0..5);
let line_count = rng.random_range(0..5);
log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
@@ -3664,8 +3665,8 @@ impl MultiBuffer {
}
let excerpt_ids = self.excerpt_ids();
if excerpt_ids.is_empty() || (rng.r#gen() && excerpt_ids.len() < max_excerpts) {
let buffer_handle = if rng.r#gen() || self.buffers.borrow().is_empty() {
if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) {
let buffer_handle = if rng.random() || self.buffers.borrow().is_empty() {
let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
buffers.push(cx.new(|cx| Buffer::local(text, cx)));
let buffer = buffers.last().unwrap().read(cx);
@@ -3687,11 +3688,11 @@ impl MultiBuffer {
let buffer = buffer_handle.read(cx);
let buffer_text = buffer.text();
let ranges = (0..rng.gen_range(0..5))
let ranges = (0..rng.random_range(0..5))
.map(|_| {
let end_ix =
buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right);
let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
ExcerptRange::new(start_ix..end_ix)
})
.collect::<Vec<_>>();
@@ -3708,7 +3709,7 @@ impl MultiBuffer {
let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
log::info!("Inserted with ids: {:?}", excerpt_id);
} else {
let remove_count = rng.gen_range(1..=excerpt_ids.len());
let remove_count = rng.random_range(1..=excerpt_ids.len());
let mut excerpts_to_remove = excerpt_ids
.choose_multiple(rng, remove_count)
.cloned()
@@ -3730,7 +3731,7 @@ impl MultiBuffer {
) {
use rand::prelude::*;
if rng.gen_bool(0.7) || self.singleton {
if rng.random_bool(0.7) || self.singleton {
let buffer = self
.buffers
.borrow()
@@ -3740,7 +3741,7 @@ impl MultiBuffer {
if let Some(buffer) = buffer {
buffer.update(cx, |buffer, cx| {
if rng.r#gen() {
if rng.random() {
buffer.randomly_edit(rng, mutation_count, cx);
} else {
buffer.randomly_undo_redo(rng, cx);
@@ -6388,8 +6389,8 @@ impl MultiBufferSnapshot {
#[cfg(any(test, feature = "test-support"))]
impl MultiBufferSnapshot {
pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
start..end
}
+26 -22
View File
@@ -2491,12 +2491,12 @@ async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
for _ in 0..operations {
let snapshot = buf.update(cx, |buf, _| buf.snapshot());
let num_ranges = rng.gen_range(0..=10);
let num_ranges = rng.random_range(0..=10);
let max_row = snapshot.max_point().row;
let mut ranges = (0..num_ranges)
.map(|_| {
let start = rng.gen_range(0..max_row);
let end = rng.gen_range(start + 1..max_row + 1);
let start = rng.random_range(0..max_row);
let end = rng.random_range(start + 1..max_row + 1);
Point::row_range(start..end)
})
.collect::<Vec<_>>();
@@ -2562,11 +2562,11 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
let mut needs_diff_calculation = false;
for _ in 0..operations {
match rng.gen_range(0..100) {
match rng.random_range(0..100) {
0..=14 if !buffers.is_empty() => {
let buffer = buffers.choose(&mut rng).unwrap();
buffer.update(cx, |buf, cx| {
let edit_count = rng.gen_range(1..5);
let edit_count = rng.random_range(1..5);
buf.randomly_edit(&mut rng, edit_count, cx);
log::info!("buffer text:\n{}", buf.text());
needs_diff_calculation = true;
@@ -2577,11 +2577,11 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
multibuffer.update(cx, |multibuffer, cx| {
let ids = multibuffer.excerpt_ids();
let mut excerpts = HashSet::default();
for _ in 0..rng.gen_range(0..ids.len()) {
for _ in 0..rng.random_range(0..ids.len()) {
excerpts.extend(ids.choose(&mut rng).copied());
}
let line_count = rng.gen_range(0..5);
let line_count = rng.random_range(0..5);
let excerpt_ixs = excerpts
.iter()
@@ -2600,7 +2600,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
}
20..=29 if !reference.excerpts.is_empty() => {
let mut ids_to_remove = vec![];
for _ in 0..rng.gen_range(1..=3) {
for _ in 0..rng.random_range(1..=3) {
let Some(excerpt) = reference.excerpts.choose(&mut rng) else {
break;
};
@@ -2620,8 +2620,12 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
let multibuffer =
multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
let offset =
multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left);
let bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
multibuffer.clip_offset(rng.random_range(0..=multibuffer.len()), Bias::Left);
let bias = if rng.random() {
Bias::Left
} else {
Bias::Right
};
log::info!("Creating anchor at {} with bias {:?}", offset, bias);
anchors.push(multibuffer.anchor_at(offset, bias));
anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
@@ -2654,7 +2658,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
45..=55 if !reference.excerpts.is_empty() => {
multibuffer.update(cx, |multibuffer, cx| {
let snapshot = multibuffer.snapshot(cx);
let excerpt_ix = rng.gen_range(0..reference.excerpts.len());
let excerpt_ix = rng.random_range(0..reference.excerpts.len());
let excerpt = &reference.excerpts[excerpt_ix];
let start = excerpt.range.start;
let end = excerpt.range.end;
@@ -2691,7 +2695,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
});
}
_ => {
let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
let buffer_handle = if buffers.is_empty() || rng.random_bool(0.4) {
let mut base_text = util::RandomCharIter::new(&mut rng)
.take(256)
.collect::<String>();
@@ -2708,7 +2712,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
buffers.choose(&mut rng).unwrap()
};
let prev_excerpt_ix = rng.gen_range(0..=reference.excerpts.len());
let prev_excerpt_ix = rng.random_range(0..=reference.excerpts.len());
let prev_excerpt_id = reference
.excerpts
.get(prev_excerpt_ix)
@@ -2716,8 +2720,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
let excerpt_ix = (prev_excerpt_ix + 1).min(reference.excerpts.len());
let (range, anchor_range) = buffer_handle.read_with(cx, |buffer, _| {
let end_row = rng.gen_range(0..=buffer.max_point().row);
let start_row = rng.gen_range(0..=end_row);
let end_row = rng.random_range(0..=buffer.max_point().row);
let start_row = rng.random_range(0..=end_row);
let end_ix = buffer.point_to_offset(Point::new(end_row, 0));
let start_ix = buffer.point_to_offset(Point::new(start_row, 0));
let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
@@ -2766,7 +2770,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
}
}
if rng.gen_bool(0.3) {
if rng.random_bool(0.3) {
multibuffer.update(cx, |multibuffer, cx| {
old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
})
@@ -2815,7 +2819,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
pretty_assertions::assert_eq!(actual_row_infos, expected_row_infos);
for _ in 0..5 {
let start_row = rng.gen_range(0..=expected_row_infos.len());
let start_row = rng.random_range(0..=expected_row_infos.len());
assert_eq!(
snapshot
.row_infos(MultiBufferRow(start_row as u32))
@@ -2872,8 +2876,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
let text_rope = Rope::from(expected_text.as_str());
for _ in 0..10 {
let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
let text_for_range = snapshot
.text_for_range(start_ix..end_ix)
@@ -2908,7 +2912,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
}
for _ in 0..10 {
let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
assert_eq!(
snapshot.reversed_chars_at(end_ix).collect::<String>(),
expected_text[..end_ix].chars().rev().collect::<String>(),
@@ -2916,8 +2920,8 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
}
for _ in 0..10 {
let end_ix = rng.gen_range(0..=text_rope.len());
let start_ix = rng.gen_range(0..=end_ix);
let end_ix = rng.random_range(0..=text_rope.len());
let start_ix = rng.random_range(0..=end_ix);
assert_eq!(
snapshot
.bytes_in_range(start_ix..end_ix)
+2 -2
View File
@@ -3761,7 +3761,7 @@ impl LspStore {
worktree_store,
languages: languages.clone(),
language_server_statuses: Default::default(),
nonce: StdRng::from_entropy().r#gen(),
nonce: StdRng::from_os_rng().random(),
diagnostic_summaries: HashMap::default(),
lsp_server_capabilities: HashMap::default(),
lsp_document_colors: HashMap::default(),
@@ -3823,7 +3823,7 @@ impl LspStore {
worktree_store,
languages: languages.clone(),
language_server_statuses: Default::default(),
nonce: StdRng::from_entropy().r#gen(),
nonce: StdRng::from_os_rng().random(),
diagnostic_summaries: HashMap::default(),
lsp_server_capabilities: HashMap::default(),
lsp_document_colors: HashMap::default(),
+3 -3
View File
@@ -7661,7 +7661,7 @@ async fn test_staging_random_hunks(
.unwrap_or(20);
// Try to induce races between diff recalculation and index writes.
if rng.gen_bool(0.5) {
if rng.random_bool(0.5) {
executor.deprioritize(*CALCULATE_DIFF_TASK);
}
@@ -7717,7 +7717,7 @@ async fn test_staging_random_hunks(
assert_eq!(hunks.len(), 6);
for _i in 0..operations {
let hunk_ix = rng.gen_range(0..hunks.len());
let hunk_ix = rng.random_range(0..hunks.len());
let hunk = &mut hunks[hunk_ix];
let row = hunk.range.start.row;
@@ -7735,7 +7735,7 @@ async fn test_staging_random_hunks(
hunk.secondary_status = SecondaryHunkAdditionPending;
}
for _ in 0..rng.gen_range(0..10) {
for _ in 0..rng.random_range(0..10) {
log::info!("yielding");
cx.executor().simulate_random_delay().await;
}
+3 -3
View File
@@ -28,11 +28,11 @@ fn generate_random_rope_ranges(mut rng: StdRng, rope: &Rope) -> Vec<Range<usize>
let mut start = 0;
for _ in 0..num_ranges {
let range_start = rope.clip_offset(
rng.gen_range(start..=(start + range_max_len)),
rng.random_range(start..=(start + range_max_len)),
sum_tree::Bias::Left,
);
let range_end = rope.clip_offset(
rng.gen_range(range_start..(range_start + range_max_len)),
rng.random_range(range_start..(range_start + range_max_len)),
sum_tree::Bias::Right,
);
@@ -52,7 +52,7 @@ fn generate_random_rope_points(mut rng: StdRng, rope: &Rope) -> Vec<Point> {
let mut points = Vec::new();
for _ in 0..num_points {
points.push(rope.offset_to_point(rng.gen_range(0..rope.len())));
points.push(rope.offset_to_point(rng.random_range(0..rope.len())));
}
points
}
+4 -4
View File
@@ -612,7 +612,7 @@ mod tests {
#[gpui::test(iterations = 100)]
fn test_random_chunks(mut rng: StdRng) {
let chunk_len = rng.gen_range(0..=MAX_BASE);
let chunk_len = rng.random_range(0..=MAX_BASE);
let text = RandomCharIter::new(&mut rng)
.take(chunk_len)
.collect::<String>();
@@ -627,8 +627,8 @@ mod tests {
verify_chunk(chunk.as_slice(), text);
for _ in 0..10 {
let mut start = rng.gen_range(0..=chunk.text.len());
let mut end = rng.gen_range(start..=chunk.text.len());
let mut start = rng.random_range(0..=chunk.text.len());
let mut end = rng.random_range(start..=chunk.text.len());
while !chunk.text.is_char_boundary(start) {
start -= 1;
}
@@ -645,7 +645,7 @@ mod tests {
#[gpui::test(iterations = 1000)]
fn test_nth_set_bit_random(mut rng: StdRng) {
let set_count = rng.gen_range(0..=128);
let set_count = rng.random_range(0..=128);
let mut set_bits = (0..128).choose_multiple(&mut rng, set_count);
set_bits.sort();
let mut n = 0;
+11 -11
View File
@@ -1610,9 +1610,9 @@ mod tests {
let mut expected = String::new();
let mut actual = Rope::new();
for _ in 0..operations {
let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
let len = rng.gen_range(0..=64);
let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
let len = rng.random_range(0..=64);
let new_text: String = RandomCharIter::new(&mut rng).take(len).collect();
let mut new_actual = Rope::new();
@@ -1629,8 +1629,8 @@ mod tests {
log::info!("text: {:?}", expected);
for _ in 0..5 {
let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
let actual_text = actual.chunks_in_range(start_ix..end_ix).collect::<String>();
assert_eq!(actual_text, &expected[start_ix..end_ix]);
@@ -1695,14 +1695,14 @@ mod tests {
);
// Check that next_line/prev_line work correctly from random positions
let mut offset = rng.gen_range(start_ix..=end_ix);
let mut offset = rng.random_range(start_ix..=end_ix);
while !expected.is_char_boundary(offset) {
offset -= 1;
}
chunks.seek(offset);
for _ in 0..5 {
if rng.r#gen() {
if rng.random() {
let expected_next_line_start = expected[offset..end_ix]
.find('\n')
.map(|newline_ix| offset + newline_ix + 1);
@@ -1791,8 +1791,8 @@ mod tests {
}
assert!((start_ix..=end_ix).contains(&chunks.offset()));
if rng.r#gen() {
offset = rng.gen_range(start_ix..=end_ix);
if rng.random() {
offset = rng.random_range(start_ix..=end_ix);
while !expected.is_char_boundary(offset) {
offset -= 1;
}
@@ -1876,8 +1876,8 @@ mod tests {
}
for _ in 0..5 {
let end_ix = clip_offset(&expected, rng.gen_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.gen_range(0..=end_ix), Left);
let end_ix = clip_offset(&expected, rng.random_range(0..=expected.len()), Right);
let start_ix = clip_offset(&expected, rng.random_range(0..=end_ix), Left);
assert_eq!(
actual.cursor(start_ix).summary::<TextSummary>(end_ix),
TextSummary::from(&expected[start_ix..end_ix])
+35 -5
View File
@@ -1,6 +1,6 @@
use anyhow::{Context as _, Result};
use base64::prelude::*;
use rand::{Rng as _, thread_rng};
use rand::prelude::*;
use rsa::pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey};
use rsa::traits::PaddingScheme;
use rsa::{Oaep, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
@@ -31,7 +31,7 @@ pub struct PrivateKey(RsaPrivateKey);
/// Generate a public and private key for asymmetric encryption.
pub fn keypair() -> Result<(PublicKey, PrivateKey)> {
let mut rng = thread_rng();
let mut rng = RsaRngCompat::new();
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits)?;
let public_key = RsaPublicKey::from(&private_key);
@@ -40,10 +40,10 @@ pub fn keypair() -> Result<(PublicKey, PrivateKey)> {
/// Generate a random 64-character base64 string.
pub fn random_token() -> String {
let mut rng = thread_rng();
let mut rng = rand::rng();
let mut token_bytes = [0; 48];
for byte in token_bytes.iter_mut() {
*byte = rng.r#gen();
*byte = rng.random();
}
BASE64_URL_SAFE.encode(token_bytes)
}
@@ -52,7 +52,7 @@ impl PublicKey {
/// Convert a string to a base64-encoded string that can only be decoded with the corresponding
/// private key.
pub fn encrypt_string(&self, string: &str, format: EncryptionFormat) -> Result<String> {
let mut rng = thread_rng();
let mut rng = RsaRngCompat::new();
let bytes = string.as_bytes();
let encrypted_bytes = match format {
EncryptionFormat::V0 => self.0.encrypt(&mut rng, Pkcs1v15Encrypt, bytes),
@@ -107,6 +107,36 @@ impl TryFrom<String> for PublicKey {
}
}
// TODO: remove once we rsa v0.10 is released.
struct RsaRngCompat(rand::rngs::ThreadRng);
impl RsaRngCompat {
fn new() -> Self {
Self(rand::rng())
}
}
impl rsa::signature::rand_core::RngCore for RsaRngCompat {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rsa::signature::rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
impl rsa::signature::rand_core::CryptoRng for RsaRngCompat {}
#[cfg(test)]
mod tests {
use super::*;
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "scheduler"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "Apache-2.0"
[lints]
workspace = true
[lib]
path = "src/scheduler.rs"
doctest = false
[features]
test-support = []
[dependencies]
async-task.workspace = true
chrono.workspace = true
futures.workspace = true
parking.workspace = true
parking_lot.workspace = true
rand.workspace = true
workspace-hack.workspace = true
+1
View File
@@ -0,0 +1 @@
../../LICENSE-APACHE
+34
View File
@@ -0,0 +1,34 @@
use chrono::{DateTime, Duration, Utc};
use parking_lot::Mutex;
pub trait Clock {
fn now(&self) -> DateTime<Utc>;
}
pub struct TestClock {
now: Mutex<DateTime<Utc>>,
}
impl TestClock {
pub fn new() -> Self {
const START_TIME: &str = "2025-07-01T23:59:58-00:00";
let now = DateTime::parse_from_rfc3339(START_TIME).unwrap().to_utc();
Self {
now: Mutex::new(now),
}
}
pub fn set_now(&self, now: DateTime<Utc>) {
*self.now.lock() = now;
}
pub fn advance(&self, duration: Duration) {
*self.now.lock() += duration;
}
}
impl Clock for TestClock {
fn now(&self) -> DateTime<Utc> {
*self.now.lock()
}
}
+137
View File
@@ -0,0 +1,137 @@
use crate::{Scheduler, SessionId, Timer};
use std::{
future::Future,
marker::PhantomData,
pin::Pin,
rc::Rc,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
#[derive(Clone)]
pub struct ForegroundExecutor {
session_id: SessionId,
scheduler: Arc<dyn Scheduler>,
not_send: PhantomData<Rc<()>>,
}
impl ForegroundExecutor {
pub fn spawn<F>(&self, future: F) -> Task<F::Output>
where
F: Future + 'static,
F::Output: 'static,
{
let session_id = self.session_id;
let scheduler = Arc::clone(&self.scheduler);
let (runnable, task) = async_task::spawn_local(future, move |runnable| {
scheduler.schedule_foreground(session_id, runnable);
});
runnable.schedule();
Task(TaskState::Spawned(task))
}
pub fn timer(&self, duration: Duration) -> Timer {
self.scheduler.timer(duration)
}
}
impl ForegroundExecutor {
pub fn new(session_id: SessionId, scheduler: Arc<dyn Scheduler>) -> Self {
assert!(
scheduler.is_main_thread(),
"ForegroundExecutor must be created on the same thread as the Scheduler"
);
Self {
session_id,
scheduler,
not_send: PhantomData,
}
}
}
impl BackgroundExecutor {
pub fn new(scheduler: Arc<dyn Scheduler>) -> Self {
Self { scheduler }
}
}
pub struct BackgroundExecutor {
scheduler: Arc<dyn Scheduler>,
}
impl BackgroundExecutor {
pub fn spawn<F>(&self, future: F) -> Task<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let scheduler = Arc::clone(&self.scheduler);
let (runnable, task) = async_task::spawn(future, move |runnable| {
scheduler.schedule_background(runnable);
});
runnable.schedule();
Task(TaskState::Spawned(task))
}
pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
self.scheduler.block_on(future)
}
pub fn block_with_timeout<Fut: Unpin + Future>(
&self,
future: &mut Fut,
timeout: Duration,
) -> Option<Fut::Output> {
self.scheduler.block_with_timeout(future, timeout)
}
pub fn timer(&self, duration: Duration) -> Timer {
self.scheduler.timer(duration)
}
}
/// Task is a primitive that allows work to happen in the background.
///
/// It implements [`Future`] so you can `.await` on it.
///
/// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
/// the task to continue running, but with no way to return a value.
#[must_use]
#[derive(Debug)]
pub struct Task<T>(TaskState<T>);
#[derive(Debug)]
enum TaskState<T> {
/// A task that is ready to return a value
Ready(Option<T>),
/// A task that is currently running.
Spawned(async_task::Task<T>),
}
impl<T> Task<T> {
/// Creates a new task that will resolve with the value
pub fn ready(val: T) -> Self {
Task(TaskState::Ready(Some(val)))
}
/// Detaching a task runs it to completion in the background
pub fn detach(self) {
match self {
Task(TaskState::Ready(_)) => {}
Task(TaskState::Spawned(task)) => task.detach(),
}
}
}
impl<T> Future for Task<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
match unsafe { self.get_unchecked_mut() } {
Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
Task(TaskState::Spawned(task)) => Pin::new(task).poll(cx),
}
}
}
+63
View File
@@ -0,0 +1,63 @@
mod clock;
mod executor;
mod test_scheduler;
#[cfg(test)]
mod tests;
pub use clock::*;
pub use executor::*;
pub use test_scheduler::*;
use async_task::Runnable;
use futures::{FutureExt as _, channel::oneshot, future::LocalBoxFuture};
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::Duration,
};
pub trait Scheduler: Send + Sync {
fn block(&self, future: LocalBoxFuture<()>, timeout: Option<Duration>);
fn schedule_foreground(&self, session_id: SessionId, runnable: Runnable);
fn schedule_background(&self, runnable: Runnable);
fn timer(&self, timeout: Duration) -> Timer;
fn is_main_thread(&self) -> bool;
}
impl dyn Scheduler {
pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
let mut output = None;
self.block(async { output = Some(future.await) }.boxed_local(), None);
output.unwrap()
}
pub fn block_with_timeout<Fut: Unpin + Future>(
&self,
future: &mut Fut,
timeout: Duration,
) -> Option<Fut::Output> {
let mut output = None;
self.block(
async { output = Some(future.await) }.boxed_local(),
Some(timeout),
);
output
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct SessionId(u16);
pub struct Timer(oneshot::Receiver<()>);
impl Future for Timer {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
match self.0.poll_unpin(cx) {
Poll::Ready(_) => Poll::Ready(()),
Poll::Pending => Poll::Pending,
}
}
}
+352
View File
@@ -0,0 +1,352 @@
use crate::{
BackgroundExecutor, Clock as _, ForegroundExecutor, Scheduler, SessionId, TestClock, Timer,
};
use async_task::Runnable;
use chrono::{DateTime, Duration as ChronoDuration, Utc};
use futures::{FutureExt as _, channel::oneshot, future::LocalBoxFuture};
use parking_lot::Mutex;
use rand::prelude::*;
use std::{
collections::VecDeque,
future::Future,
panic::{self, AssertUnwindSafe},
pin::Pin,
sync::{
Arc,
atomic::{AtomicBool, Ordering::SeqCst},
},
task::{Context, Poll, Wake, Waker},
thread,
time::{Duration, Instant},
};
pub struct TestScheduler {
clock: Arc<TestClock>,
rng: Arc<Mutex<StdRng>>,
state: Mutex<SchedulerState>,
pub thread_id: thread::ThreadId,
pub config: SchedulerConfig,
}
impl TestScheduler {
/// Run a test once with default configuration (seed 0)
pub fn once<R>(f: impl AsyncFnOnce(Arc<TestScheduler>) -> R) -> R {
Self::with_seed(0, f)
}
/// Run a test multiple times with sequential seeds (0, 1, 2, ...)
pub fn many<R>(iterations: usize, mut f: impl AsyncFnMut(Arc<TestScheduler>) -> R) -> Vec<R> {
(0..iterations as u64)
.map(|seed| {
let mut unwind_safe_f = AssertUnwindSafe(&mut f);
match panic::catch_unwind(move || Self::with_seed(seed, &mut *unwind_safe_f)) {
Ok(result) => result,
Err(error) => {
eprintln!("Failing Seed: {seed}");
panic::resume_unwind(error);
}
}
})
.collect()
}
/// Run a test once with a specific seed
pub fn with_seed<R>(seed: u64, f: impl AsyncFnOnce(Arc<TestScheduler>) -> R) -> R {
let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::with_seed(seed)));
let future = f(scheduler.clone());
let result = scheduler.block_on(future);
scheduler.run();
result
}
pub fn new(config: SchedulerConfig) -> Self {
Self {
rng: Arc::new(Mutex::new(StdRng::seed_from_u64(config.seed))),
state: Mutex::new(SchedulerState {
runnables: VecDeque::new(),
timers: Vec::new(),
randomize_order: config.randomize_order,
allow_parking: config.allow_parking,
next_session_id: SessionId(0),
}),
thread_id: thread::current().id(),
clock: Arc::new(TestClock::new()),
config,
}
}
pub fn clock(&self) -> Arc<TestClock> {
self.clock.clone()
}
pub fn rng(&self) -> Arc<Mutex<StdRng>> {
self.rng.clone()
}
/// Create a foreground executor for this scheduler
pub fn foreground(self: &Arc<Self>) -> ForegroundExecutor {
let session_id = {
let mut state = self.state.lock();
state.next_session_id.0 += 1;
state.next_session_id
};
ForegroundExecutor::new(session_id, self.clone())
}
/// Create a background executor for this scheduler
pub fn background(self: &Arc<Self>) -> BackgroundExecutor {
BackgroundExecutor::new(self.clone())
}
pub fn block_on<Fut: Future>(&self, future: Fut) -> Fut::Output {
(self as &dyn Scheduler).block_on(future)
}
pub fn yield_random(&self) -> Yield {
Yield(self.rng.lock().random_range(0..20))
}
pub fn run(&self) {
while self.step() || self.advance_clock() {
// Continue until no work remains
}
}
fn step(&self) -> bool {
let elapsed_timers = {
let mut state = self.state.lock();
let end_ix = state
.timers
.partition_point(|timer| timer.expiration <= self.clock.now());
state.timers.drain(..end_ix).collect::<Vec<_>>()
};
if !elapsed_timers.is_empty() {
return true;
}
let runnable = self.state.lock().runnables.pop_front();
if let Some(runnable) = runnable {
runnable.run();
return true;
}
false
}
fn advance_clock(&self) -> bool {
if let Some(timer) = self.state.lock().timers.first() {
self.clock.set_now(timer.expiration);
true
} else {
false
}
}
}
impl Scheduler for TestScheduler {
fn is_main_thread(&self) -> bool {
thread::current().id() == self.thread_id
}
fn schedule_foreground(&self, session_id: SessionId, runnable: Runnable) {
let mut state = self.state.lock();
let ix = if state.randomize_order {
let start_ix = state
.runnables
.iter()
.rposition(|task| task.session_id == Some(session_id))
.map_or(0, |ix| ix + 1);
self.rng
.lock()
.random_range(start_ix..=state.runnables.len())
} else {
state.runnables.len()
};
state.runnables.insert(
ix,
ScheduledRunnable {
session_id: Some(session_id),
runnable,
},
);
}
fn schedule_background(&self, runnable: Runnable) {
let mut state = self.state.lock();
let ix = if state.randomize_order {
self.rng.lock().random_range(0..=state.runnables.len())
} else {
state.runnables.len()
};
state.runnables.insert(
ix,
ScheduledRunnable {
session_id: None,
runnable,
},
);
}
fn timer(&self, duration: Duration) -> Timer {
let (tx, rx) = oneshot::channel();
let expiration = self.clock.now() + ChronoDuration::from_std(duration).unwrap();
let state = &mut *self.state.lock();
state.timers.push(ScheduledTimer {
expiration,
_notify: tx,
});
state.timers.sort_by_key(|timer| timer.expiration);
Timer(rx)
}
/// Block until the given future completes, with an optional timeout. If the
/// future is unable to make progress at any moment before the timeout and
/// no other tasks or timers remain, we panic unless parking is allowed. If
/// parking is allowed, we block up to the timeout or indefinitely if none
/// is provided. This is to allow testing a mix of deterministic and
/// non-deterministic async behavior, such as when interacting with I/O in
/// an otherwise deterministic test.
fn block(&self, mut future: LocalBoxFuture<()>, timeout: Option<Duration>) {
let (parker, unparker) = parking::pair();
let deadline = timeout.map(|timeout| Instant::now() + timeout);
let awoken = Arc::new(AtomicBool::new(false));
let waker = Waker::from(Arc::new(WakerFn::new({
let awoken = awoken.clone();
move || {
awoken.store(true, SeqCst);
unparker.unpark();
}
})));
let max_ticks = if timeout.is_some() {
self.rng
.lock()
.random_range(0..=self.config.max_timeout_ticks)
} else {
usize::MAX
};
let mut cx = Context::from_waker(&waker);
for _ in 0..max_ticks {
let Poll::Pending = future.poll_unpin(&mut cx) else {
break;
};
let mut stepped = None;
while self.rng.lock().random() && stepped.unwrap_or(true) {
*stepped.get_or_insert(false) |= self.step();
}
let stepped = stepped.unwrap_or(true);
let awoken = awoken.swap(false, SeqCst);
if !stepped && !awoken && !self.advance_clock() {
if self.state.lock().allow_parking {
if !park(&parker, deadline) {
break;
}
} else if deadline.is_some() {
break;
} else {
panic!("Parking forbidden");
}
}
}
}
}
#[derive(Clone, Debug)]
pub struct SchedulerConfig {
pub seed: u64,
pub randomize_order: bool,
pub allow_parking: bool,
pub max_timeout_ticks: usize,
}
impl SchedulerConfig {
pub fn with_seed(seed: u64) -> Self {
Self {
seed,
..Default::default()
}
}
}
impl Default for SchedulerConfig {
fn default() -> Self {
Self {
seed: 0,
randomize_order: true,
allow_parking: false,
max_timeout_ticks: 1000,
}
}
}
struct ScheduledRunnable {
session_id: Option<SessionId>,
runnable: Runnable,
}
impl ScheduledRunnable {
fn run(self) {
self.runnable.run();
}
}
struct ScheduledTimer {
expiration: DateTime<Utc>,
_notify: oneshot::Sender<()>,
}
struct SchedulerState {
runnables: VecDeque<ScheduledRunnable>,
timers: Vec<ScheduledTimer>,
randomize_order: bool,
allow_parking: bool,
next_session_id: SessionId,
}
struct WakerFn<F> {
f: F,
}
impl<F: Fn()> WakerFn<F> {
fn new(f: F) -> Self {
Self { f }
}
}
impl<F: Fn()> Wake for WakerFn<F> {
fn wake(self: Arc<Self>) {
(self.f)();
}
fn wake_by_ref(self: &Arc<Self>) {
(self.f)();
}
}
pub struct Yield(usize);
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if self.0 == 0 {
Poll::Ready(())
} else {
self.0 -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
fn park(parker: &parking::Parker, deadline: Option<Instant>) -> bool {
if let Some(deadline) = deadline {
parker.park_deadline(deadline)
} else {
parker.park();
true
}
}
+348
View File
@@ -0,0 +1,348 @@
use super::*;
use futures::{
FutureExt,
channel::{mpsc, oneshot},
executor::block_on,
future,
sink::SinkExt,
stream::{FuturesUnordered, StreamExt},
};
use std::{
cell::RefCell,
collections::{BTreeSet, HashSet},
pin::Pin,
rc::Rc,
sync::Arc,
task::{Context, Poll},
};
#[test]
fn test_foreground_executor_spawn() {
let result = TestScheduler::once(async |scheduler| {
let task = scheduler.foreground().spawn(async move { 42 });
task.await
});
assert_eq!(result, 42);
}
#[test]
fn test_background_executor_spawn() {
TestScheduler::once(async |scheduler| {
let task = scheduler.background().spawn(async move { 42 });
let result = task.await;
assert_eq!(result, 42);
});
}
#[test]
fn test_foreground_ordering() {
let mut traces = HashSet::new();
TestScheduler::many(100, async |scheduler| {
#[derive(Hash, PartialEq, Eq)]
struct TraceEntry {
session: usize,
task: usize,
}
let trace = Rc::new(RefCell::new(Vec::new()));
let foreground_1 = scheduler.foreground();
for task in 0..10 {
foreground_1
.spawn({
let trace = trace.clone();
async move {
trace.borrow_mut().push(TraceEntry { session: 0, task });
}
})
.detach();
}
let foreground_2 = scheduler.foreground();
for task in 0..10 {
foreground_2
.spawn({
let trace = trace.clone();
async move {
trace.borrow_mut().push(TraceEntry { session: 1, task });
}
})
.detach();
}
scheduler.run();
assert_eq!(
trace
.borrow()
.iter()
.filter(|entry| entry.session == 0)
.map(|entry| entry.task)
.collect::<Vec<_>>(),
(0..10).collect::<Vec<_>>()
);
assert_eq!(
trace
.borrow()
.iter()
.filter(|entry| entry.session == 1)
.map(|entry| entry.task)
.collect::<Vec<_>>(),
(0..10).collect::<Vec<_>>()
);
traces.insert(trace.take());
});
assert!(traces.len() > 1, "Expected at least two traces");
}
#[test]
fn test_timer_ordering() {
TestScheduler::many(1, async |scheduler| {
let background = scheduler.background();
let futures = FuturesUnordered::new();
futures.push(
async {
background.timer(Duration::from_millis(100)).await;
2
}
.boxed(),
);
futures.push(
async {
background.timer(Duration::from_millis(50)).await;
1
}
.boxed(),
);
futures.push(
async {
background.timer(Duration::from_millis(150)).await;
3
}
.boxed(),
);
assert_eq!(futures.collect::<Vec<_>>().await, vec![1, 2, 3]);
});
}
#[test]
fn test_send_from_bg_to_fg() {
TestScheduler::once(async |scheduler| {
let foreground = scheduler.foreground();
let background = scheduler.background();
let (sender, receiver) = oneshot::channel::<i32>();
background
.spawn(async move {
sender.send(42).unwrap();
})
.detach();
let task = foreground.spawn(async move { receiver.await.unwrap() });
let result = task.await;
assert_eq!(result, 42);
});
}
#[test]
fn test_randomize_order() {
// Test deterministic mode: different seeds should produce same execution order
let mut deterministic_results = HashSet::new();
for seed in 0..10 {
let config = SchedulerConfig {
seed,
randomize_order: false,
..Default::default()
};
let order = block_on(capture_execution_order(config));
assert_eq!(order.len(), 6);
deterministic_results.insert(order);
}
// All deterministic runs should produce the same result
assert_eq!(
deterministic_results.len(),
1,
"Deterministic mode should always produce same execution order"
);
// Test randomized mode: different seeds can produce different execution orders
let mut randomized_results = HashSet::new();
for seed in 0..20 {
let config = SchedulerConfig::with_seed(seed);
let order = block_on(capture_execution_order(config));
assert_eq!(order.len(), 6);
randomized_results.insert(order);
}
// Randomized mode should produce multiple different execution orders
assert!(
randomized_results.len() > 1,
"Randomized mode should produce multiple different orders"
);
}
async fn capture_execution_order(config: SchedulerConfig) -> Vec<String> {
let scheduler = Arc::new(TestScheduler::new(config));
let foreground = scheduler.foreground();
let background = scheduler.background();
let (sender, receiver) = mpsc::unbounded::<String>();
// Spawn foreground tasks
for i in 0..3 {
let mut sender = sender.clone();
foreground
.spawn(async move {
sender.send(format!("fg-{}", i)).await.ok();
})
.detach();
}
// Spawn background tasks
for i in 0..3 {
let mut sender = sender.clone();
background
.spawn(async move {
sender.send(format!("bg-{}", i)).await.ok();
})
.detach();
}
drop(sender); // Close sender to signal no more messages
scheduler.run();
receiver.collect().await
}
#[test]
fn test_block() {
let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
let executor = BackgroundExecutor::new(scheduler);
let (tx, rx) = oneshot::channel();
// Spawn background task to send value
let _ = executor
.spawn(async move {
tx.send(42).unwrap();
})
.detach();
// Block on receiving the value
let result = executor.block_on(async { rx.await.unwrap() });
assert_eq!(result, 42);
}
#[test]
#[should_panic(expected = "Parking forbidden")]
fn test_parking_panics() {
let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
let executor = BackgroundExecutor::new(scheduler);
executor.block_on(future::pending::<()>());
}
#[test]
fn test_block_with_parking() {
let config = SchedulerConfig {
allow_parking: true,
..Default::default()
};
let scheduler = Arc::new(TestScheduler::new(config));
let executor = BackgroundExecutor::new(scheduler);
let (tx, rx) = oneshot::channel();
// Spawn background task to send value
let _ = executor
.spawn(async move {
tx.send(42).unwrap();
})
.detach();
// Block on receiving the value (will park if needed)
let result = executor.block_on(async { rx.await.unwrap() });
assert_eq!(result, 42);
}
#[test]
fn test_helper_methods() {
// Test the once method
let result = TestScheduler::once(async |scheduler: Arc<TestScheduler>| {
let background = scheduler.background();
background.spawn(async { 42 }).await
});
assert_eq!(result, 42);
// Test the many method
let results = TestScheduler::many(3, async |scheduler: Arc<TestScheduler>| {
let background = scheduler.background();
background.spawn(async { 10 }).await
});
assert_eq!(results, vec![10, 10, 10]);
// Test the with_seed method
let result = TestScheduler::with_seed(123, async |scheduler: Arc<TestScheduler>| {
let background = scheduler.background();
// Spawn a background task and wait for its result
let task = background.spawn(async { 99 });
task.await
});
assert_eq!(result, 99);
}
#[test]
fn test_block_with_timeout() {
// Test case: future completes within timeout
TestScheduler::once(async |scheduler| {
let background = scheduler.background();
let mut future = future::ready(42);
let output = background.block_with_timeout(&mut future, Duration::from_millis(100));
assert_eq!(output, Some(42));
});
// Test case: future times out
TestScheduler::once(async |scheduler| {
let background = scheduler.background();
let mut future = future::pending::<()>();
let output = background.block_with_timeout(&mut future, Duration::from_millis(50));
assert_eq!(output, None);
});
// Test case: future makes progress via timer but still times out
let mut results = BTreeSet::new();
TestScheduler::many(100, async |scheduler| {
let background = scheduler.background();
let mut task = background.spawn(async move {
Yield { polls: 10 }.await;
42
});
let output = background.block_with_timeout(&mut task, Duration::from_millis(50));
results.insert(output);
});
assert_eq!(
results.into_iter().collect::<Vec<_>>(),
vec![None, Some(42)]
);
}
struct Yield {
polls: usize,
}
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.polls -= 1;
if self.polls == 0 {
Poll::Ready(())
} else {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
+6 -6
View File
@@ -945,7 +945,7 @@ mod tests {
let mut new_len = 0;
while new_len < new.len() {
let mut chunk_len = rng.gen_range(1..=new.len() - new_len);
let mut chunk_len = rng.random_range(1..=new.len() - new_len);
while !new.is_char_boundary(new_len + chunk_len) {
chunk_len += 1;
}
@@ -1034,14 +1034,14 @@ mod tests {
fn randomly_edit(text: &str, rng: &mut impl Rng) -> String {
let mut result = String::from(text);
let edit_count = rng.gen_range(1..=5);
let edit_count = rng.random_range(1..=5);
fn random_char_range(text: &str, rng: &mut impl Rng) -> (usize, usize) {
let mut start = rng.gen_range(0..=text.len());
let mut start = rng.random_range(0..=text.len());
while !text.is_char_boundary(start) {
start -= 1;
}
let mut end = rng.gen_range(start..=text.len());
let mut end = rng.random_range(start..=text.len());
while !text.is_char_boundary(end) {
end += 1;
}
@@ -1049,11 +1049,11 @@ mod tests {
}
for _ in 0..edit_count {
match rng.gen_range(0..3) {
match rng.random_range(0..3) {
0 => {
// Insert
let (pos, _) = random_char_range(&result, rng);
let insert_len = rng.gen_range(1..=5);
let insert_len = rng.random_range(1..=5);
let insert_text: String = random_text(rng, insert_len);
result.insert_str(pos, &insert_text);
}
+27 -19
View File
@@ -909,7 +909,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use rand::{distributions, prelude::*};
use rand::{distr::StandardUniform, prelude::*};
use std::cmp;
#[ctor::ctor]
@@ -951,24 +951,24 @@ mod tests {
let rng = &mut rng;
let mut tree = SumTree::<u8>::default();
let count = rng.gen_range(0..10);
if rng.r#gen() {
tree.extend(rng.sample_iter(distributions::Standard).take(count), &());
let count = rng.random_range(0..10);
if rng.random() {
tree.extend(rng.sample_iter(StandardUniform).take(count), &());
} else {
let items = rng
.sample_iter(distributions::Standard)
.sample_iter(StandardUniform)
.take(count)
.collect::<Vec<_>>();
tree.par_extend(items, &());
}
for _ in 0..num_operations {
let splice_end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
let splice_start = rng.gen_range(0..splice_end + 1);
let count = rng.gen_range(0..10);
let splice_end = rng.random_range(0..tree.extent::<Count>(&()).0 + 1);
let splice_start = rng.random_range(0..splice_end + 1);
let count = rng.random_range(0..10);
let tree_end = tree.extent::<Count>(&());
let new_items = rng
.sample_iter(distributions::Standard)
.sample_iter(StandardUniform)
.take(count)
.collect::<Vec<u8>>();
@@ -978,7 +978,7 @@ mod tests {
tree = {
let mut cursor = tree.cursor::<Count>(&());
let mut new_tree = cursor.slice(&Count(splice_start), Bias::Right);
if rng.r#gen() {
if rng.random() {
new_tree.extend(new_items, &());
} else {
new_tree.par_extend(new_items, &());
@@ -1005,7 +1005,7 @@ mod tests {
.filter(|(_, item)| (item & 1) == 0)
.collect::<Vec<_>>();
let mut item_ix = if rng.r#gen() {
let mut item_ix = if rng.random() {
filter_cursor.next();
0
} else {
@@ -1022,12 +1022,12 @@ mod tests {
filter_cursor.next();
item_ix += 1;
while item_ix > 0 && rng.gen_bool(0.2) {
while item_ix > 0 && rng.random_bool(0.2) {
log::info!("prev");
filter_cursor.prev();
item_ix -= 1;
if item_ix == 0 && rng.gen_bool(0.2) {
if item_ix == 0 && rng.random_bool(0.2) {
filter_cursor.prev();
assert_eq!(filter_cursor.item(), None);
assert_eq!(filter_cursor.start().0, 0);
@@ -1039,9 +1039,9 @@ mod tests {
let mut before_start = false;
let mut cursor = tree.cursor::<Count>(&());
let start_pos = rng.gen_range(0..=reference_items.len());
let start_pos = rng.random_range(0..=reference_items.len());
cursor.seek(&Count(start_pos), Bias::Right);
let mut pos = rng.gen_range(start_pos..=reference_items.len());
let mut pos = rng.random_range(start_pos..=reference_items.len());
cursor.seek_forward(&Count(pos), Bias::Right);
for i in 0..10 {
@@ -1084,10 +1084,18 @@ mod tests {
}
for _ in 0..10 {
let end = rng.gen_range(0..tree.extent::<Count>(&()).0 + 1);
let start = rng.gen_range(0..end + 1);
let start_bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
let end_bias = if rng.r#gen() { Bias::Left } else { Bias::Right };
let end = rng.random_range(0..tree.extent::<Count>(&()).0 + 1);
let start = rng.random_range(0..end + 1);
let start_bias = if rng.random() {
Bias::Left
} else {
Bias::Right
};
let end_bias = if rng.random() {
Bias::Left
} else {
Bias::Right
};
let mut cursor = tree.cursor::<Count>(&());
cursor.seek(&Count(start), start_bias);
+9 -8
View File
@@ -2198,7 +2198,7 @@ mod tests {
};
use collections::HashMap;
use gpui::{Pixels, Point, TestAppContext, bounds, point, size};
use rand::{Rng, distributions::Alphanumeric, rngs::ThreadRng, thread_rng};
use rand::{Rng, distr, rngs::ThreadRng};
#[ignore = "Test is flaky on macOS, and doesn't run on Windows"]
#[gpui::test]
@@ -2249,13 +2249,14 @@ mod tests {
#[test]
fn test_mouse_to_cell_test() {
let mut rng = thread_rng();
let mut rng = rand::rng();
const ITERATIONS: usize = 10;
const PRECISION: usize = 1000;
for _ in 0..ITERATIONS {
let viewport_cells = rng.gen_range(15..20);
let cell_size = rng.gen_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
let viewport_cells = rng.random_range(15..20);
let cell_size =
rng.random_range(5 * PRECISION..20 * PRECISION) as f32 / PRECISION as f32;
let size = crate::TerminalBounds {
cell_width: Pixels::from(cell_size),
@@ -2277,8 +2278,8 @@ mod tests {
for col in 0..(viewport_cells - 1) {
let col = col as usize;
let row_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
let col_offset = rng.gen_range(0..PRECISION) as f32 / PRECISION as f32;
let row_offset = rng.random_range(0..PRECISION) as f32 / PRECISION as f32;
let col_offset = rng.random_range(0..PRECISION) as f32 / PRECISION as f32;
let mouse_pos = point(
Pixels::from(col as f32 * cell_size + col_offset),
@@ -2298,7 +2299,7 @@ mod tests {
#[test]
fn test_mouse_to_cell_clamp() {
let mut rng = thread_rng();
let mut rng = rand::rng();
let size = crate::TerminalBounds {
cell_width: Pixels::from(10.),
@@ -2336,7 +2337,7 @@ mod tests {
for _ in 0..((size.height() / size.line_height()) as usize) {
let mut row_vec = Vec::new();
for _ in 0..((size.width() / size.cell_width()) as usize) {
let cell_char = rng.sample(Alphanumeric) as char;
let cell_char = rng.sample(distr::Alphanumeric) as char;
row_vec.push(cell_char)
}
cells.push(row_vec)
+4 -4
View File
@@ -106,13 +106,13 @@ mod tests {
let mut rhs = Default::default();
while lhs == rhs {
lhs = Locator(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
(0..rng.random_range(1..=5))
.map(|_| rng.random_range(0..=100))
.collect(),
);
rhs = Locator(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
(0..rng.random_range(1..=5))
.map(|_| rng.random_range(0..=100))
.collect(),
);
}
+3 -3
View File
@@ -65,8 +65,8 @@ impl<T: Clone, R: rand::Rng> Network<T, R> {
for message in &messages {
// Insert one or more duplicates of this message, potentially *before* the previous
// message sent by this peer to simulate out-of-order delivery.
for _ in 0..self.rng.gen_range(1..4) {
let insertion_index = self.rng.gen_range(0..inbox.len() + 1);
for _ in 0..self.rng.random_range(1..4) {
let insertion_index = self.rng.random_range(0..inbox.len() + 1);
inbox.insert(
insertion_index,
Envelope {
@@ -85,7 +85,7 @@ impl<T: Clone, R: rand::Rng> Network<T, R> {
pub fn receive(&mut self, receiver: ReplicaId) -> Vec<T> {
let inbox = self.inboxes.get_mut(&receiver).unwrap();
let count = self.rng.gen_range(0..inbox.len() + 1);
let count = self.rng.random_range(0..inbox.len() + 1);
inbox
.drain(0..count)
.map(|envelope| envelope.message)
+6 -6
View File
@@ -497,8 +497,8 @@ mod tests {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(20);
let initial_chars = (0..rng.gen_range(0..=100))
.map(|_| rng.gen_range(b'a'..=b'z') as char)
let initial_chars = (0..rng.random_range(0..=100))
.map(|_| rng.random_range(b'a'..=b'z') as char)
.collect::<Vec<_>>();
log::info!("initial chars: {:?}", initial_chars);
@@ -517,11 +517,11 @@ mod tests {
break;
}
let end = rng.gen_range(last_edit_end..=expected_chars.len());
let start = rng.gen_range(last_edit_end..=end);
let end = rng.random_range(last_edit_end..=expected_chars.len());
let start = rng.random_range(last_edit_end..=end);
let old_len = end - start;
let mut new_len = rng.gen_range(0..=3);
let mut new_len = rng.random_range(0..=3);
if start == end && new_len == 0 {
new_len += 1;
}
@@ -529,7 +529,7 @@ mod tests {
last_edit_end = start + new_len + 1;
let new_chars = (0..new_len)
.map(|_| rng.gen_range(b'A'..=b'Z') as char)
.map(|_| rng.random_range(b'A'..=b'Z') as char)
.collect::<Vec<_>>();
log::info!(
" editing {:?}: {:?}",
+11 -10
View File
@@ -36,14 +36,14 @@ fn test_random_edits(mut rng: StdRng) {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let reference_string_len = rng.gen_range(0..3);
let reference_string_len = rng.random_range(0..3);
let mut reference_string = RandomCharIter::new(&mut rng)
.take(reference_string_len)
.collect::<String>();
let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), reference_string.clone());
LineEnding::normalize(&mut reference_string);
buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
let mut buffer_versions = Vec::new();
log::info!(
"buffer text {:?}, version: {:?}",
@@ -64,7 +64,7 @@ fn test_random_edits(mut rng: StdRng) {
buffer.version()
);
if rng.gen_bool(0.25) {
if rng.random_bool(0.25) {
buffer.randomly_undo_redo(&mut rng);
reference_string = buffer.text();
log::info!(
@@ -82,7 +82,7 @@ fn test_random_edits(mut rng: StdRng) {
buffer.check_invariants();
if rng.gen_bool(0.3) {
if rng.random_bool(0.3) {
buffer_versions.push((buffer.clone(), buffer.subscribe()));
}
}
@@ -112,8 +112,9 @@ fn test_random_edits(mut rng: StdRng) {
);
for _ in 0..5 {
let end_ix = old_buffer.clip_offset(rng.gen_range(0..=old_buffer.len()), Bias::Right);
let start_ix = old_buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
let end_ix =
old_buffer.clip_offset(rng.random_range(0..=old_buffer.len()), Bias::Right);
let start_ix = old_buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
let range = old_buffer.anchor_before(start_ix)..old_buffer.anchor_after(end_ix);
let mut old_text = old_buffer.text_for_range(range.clone()).collect::<String>();
let edits = buffer
@@ -731,7 +732,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
.map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
.unwrap_or(10);
let base_text_len = rng.gen_range(0..10);
let base_text_len = rng.random_range(0..10);
let base_text = RandomCharIter::new(&mut rng)
.take(base_text_len)
.collect::<String>();
@@ -741,7 +742,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
for i in 0..peers {
let mut buffer = Buffer::new(i as ReplicaId, BufferId::new(1).unwrap(), base_text.clone());
buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200));
buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
buffers.push(buffer);
replica_ids.push(i as u16);
network.add_peer(i as u16);
@@ -751,10 +752,10 @@ fn test_random_concurrent_edits(mut rng: StdRng) {
let mut mutation_count = operations;
loop {
let replica_index = rng.gen_range(0..peers);
let replica_index = rng.random_range(0..peers);
let replica_id = replica_ids[replica_index];
let buffer = &mut buffers[replica_index];
match rng.gen_range(0..=100) {
match rng.random_range(0..=100) {
0..=50 if mutation_count != 0 => {
let op = buffer.randomly_edit(&mut rng, 5).1;
network.broadcast(buffer.replica_id, vec![op]);
+4 -4
View File
@@ -1818,8 +1818,8 @@ impl Buffer {
}
pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
start..end
}
@@ -1841,7 +1841,7 @@ impl Buffer {
let range = self.random_byte_range(new_start, rng);
last_end = Some(range.end);
let new_text_len = rng.gen_range(0..10);
let new_text_len = rng.random_range(0..10);
let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
edits.push((range, new_text.into()));
@@ -1877,7 +1877,7 @@ impl Buffer {
use rand::prelude::*;
let mut ops = Vec::new();
for _ in 0..rng.gen_range(1..=5) {
for _ in 0..rng.random_range(1..=5) {
if let Some(entry) = self.history.undo_stack.choose(rng) {
let transaction = entry.transaction.clone();
log::info!(
+7 -6
View File
@@ -815,7 +815,8 @@ pub fn defer<F: FnOnce()>(f: F) -> Deferred<F> {
#[cfg(any(test, feature = "test-support"))]
mod rng {
use rand::{Rng, seq::SliceRandom};
use rand::prelude::*;
pub struct RandomCharIter<T: Rng> {
rng: T,
simple_text: bool,
@@ -840,18 +841,18 @@ mod rng {
fn next(&mut self) -> Option<Self::Item> {
if self.simple_text {
return if self.rng.gen_range(0..100) < 5 {
return if self.rng.random_range(0..100) < 5 {
Some('\n')
} else {
Some(self.rng.gen_range(b'a'..b'z' + 1).into())
Some(self.rng.random_range(b'a'..b'z' + 1).into())
};
}
match self.rng.gen_range(0..100) {
match self.rng.random_range(0..100) {
// whitespace
0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.rng).copied(),
// two-byte greek letters
20..=32 => char::from_u32(self.rng.gen_range(('α' as u32)..('ω' as u32 + 1))),
20..=32 => char::from_u32(self.rng.random_range(('α' as u32)..('ω' as u32 + 1))),
// // three-byte characters
33..=45 => ['✋', '✅', '❌', '❎', '⭐']
.choose(&mut self.rng)
@@ -859,7 +860,7 @@ mod rng {
// // four-byte characters
46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.rng).copied(),
// ascii letters
_ => Some(self.rng.gen_range(b'a'..b'z' + 1).into()),
_ => Some(self.rng.random_range(b'a'..b'z' + 1).into()),
}
}
}
+15 -15
View File
@@ -1464,7 +1464,7 @@ async fn test_random_worktree_operations_during_initial_scan(
tree.as_local().unwrap().snapshot().check_invariants(true)
});
if rng.gen_bool(0.6) {
if rng.random_bool(0.6) {
snapshots.push(worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot()));
}
}
@@ -1551,7 +1551,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
let mut snapshots = Vec::new();
let mut mutations_len = operations;
while mutations_len > 1 {
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
worktree
.update(cx, |worktree, cx| {
randomly_mutate_worktree(worktree, &mut rng, cx)
@@ -1563,8 +1563,8 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
}
let buffered_event_count = fs.as_fake().buffered_event_count();
if buffered_event_count > 0 && rng.gen_bool(0.3) {
let len = rng.gen_range(0..=buffered_event_count);
if buffered_event_count > 0 && rng.random_bool(0.3) {
let len = rng.random_range(0..=buffered_event_count);
log::info!("flushing {} events", len);
fs.as_fake().flush_events(len);
} else {
@@ -1573,7 +1573,7 @@ async fn test_random_worktree_changes(cx: &mut TestAppContext, mut rng: StdRng)
}
cx.executor().run_until_parked();
if rng.gen_bool(0.2) {
if rng.random_bool(0.2) {
log::info!("storing snapshot {}", snapshots.len());
let snapshot = worktree.read_with(cx, |tree, _| tree.as_local().unwrap().snapshot());
snapshots.push(snapshot);
@@ -1701,7 +1701,7 @@ fn randomly_mutate_worktree(
let snapshot = worktree.snapshot();
let entry = snapshot.entries(false, 0).choose(rng).unwrap();
match rng.gen_range(0_u32..100) {
match rng.random_range(0_u32..100) {
0..=33 if entry.path.as_ref() != Path::new("") => {
log::info!("deleting entry {:?} ({})", entry.path, entry.id.0);
worktree.delete_entry(entry.id, false, cx).unwrap()
@@ -1733,7 +1733,7 @@ fn randomly_mutate_worktree(
_ => {
if entry.is_dir() {
let child_path = entry.path.join(random_filename(rng));
let is_dir = rng.gen_bool(0.3);
let is_dir = rng.random_bool(0.3);
log::info!(
"creating {} at {:?}",
if is_dir { "dir" } else { "file" },
@@ -1776,11 +1776,11 @@ async fn randomly_mutate_fs(
}
}
if (files.is_empty() && dirs.len() == 1) || rng.gen_bool(insertion_probability) {
if (files.is_empty() && dirs.len() == 1) || rng.random_bool(insertion_probability) {
let path = dirs.choose(rng).unwrap();
let new_path = path.join(random_filename(rng));
if rng.r#gen() {
if rng.random() {
log::info!(
"creating dir {:?}",
new_path.strip_prefix(root_path).unwrap()
@@ -1793,7 +1793,7 @@ async fn randomly_mutate_fs(
);
fs.create_file(&new_path, Default::default()).await.unwrap();
}
} else if rng.gen_bool(0.05) {
} else if rng.random_bool(0.05) {
let ignore_dir_path = dirs.choose(rng).unwrap();
let ignore_path = ignore_dir_path.join(*GITIGNORE);
@@ -1808,11 +1808,11 @@ async fn randomly_mutate_fs(
.cloned()
.collect::<Vec<_>>();
let files_to_ignore = {
let len = rng.gen_range(0..=subfiles.len());
let len = rng.random_range(0..=subfiles.len());
subfiles.choose_multiple(rng, len)
};
let dirs_to_ignore = {
let len = rng.gen_range(0..subdirs.len());
let len = rng.random_range(0..subdirs.len());
subdirs.choose_multiple(rng, len)
};
@@ -1848,7 +1848,7 @@ async fn randomly_mutate_fs(
file_path.into_iter().chain(dir_path).choose(rng).unwrap()
};
let is_rename = rng.r#gen();
let is_rename = rng.random();
if is_rename {
let new_path_parent = dirs
.iter()
@@ -1857,7 +1857,7 @@ async fn randomly_mutate_fs(
.unwrap();
let overwrite_existing_dir =
!old_path.starts_with(new_path_parent) && rng.gen_bool(0.3);
!old_path.starts_with(new_path_parent) && rng.random_bool(0.3);
let new_path = if overwrite_existing_dir {
fs.remove_dir(
new_path_parent,
@@ -1919,7 +1919,7 @@ async fn randomly_mutate_fs(
fn random_filename(rng: &mut impl Rng) -> String {
(0..6)
.map(|_| rng.sample(rand::distributions::Alphanumeric))
.map(|_| rng.sample(rand::distr::Alphanumeric))
.map(char::from)
.collect()
}
+2 -2
View File
@@ -149,7 +149,7 @@ mod tests {
let mut rng = rand::thread_rng();
let mut numbers = Vec::new();
for _ in 0..5 {
numbers.push(rng.gen_range(1..101));
numbers.push(rng.random_range(1..101));
}
numbers
}
@@ -208,7 +208,7 @@ mod tests {
<|editable_region_end|>
let mut numbers = Vec::new();
for _ in 0..5 {
numbers.push(rng.gen_range(1..101));
numbers.push(rng.random_range(1..101));
```"#}
);
}