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:
co-authored by
Antonio Scandurra
parent
a05f86f97b
commit
1ae326432e
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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>()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
|
||||
@@ -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"))?;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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))),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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::*;
|
||||
|
||||
@@ -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
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../LICENSE-APACHE
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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]);
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
```"#}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user