From 0de867204428717f17e6c0e6e77b0feba826a7d8 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 22 Feb 2024 22:28:08 -0500 Subject: [PATCH] Add `SystemClock` (#8239) This PR adds a `SystemClock` trait for abstracting away the system clock. This allows us to swap out the real system clock with a `FakeSystemClock` in the tests, thus allowing the fake passage of time. We're using this in `Telemetry` to better mock the clock for testing purposes. Release Notes: - N/A --- Cargo.lock | 5 ++ crates/channel/src/channel_store_tests.rs | 4 +- crates/client/Cargo.toml | 4 +- crates/client/src/client.rs | 58 +++++++++++-- crates/client/src/telemetry.rs | 82 +++++++++++-------- crates/clock/Cargo.toml | 5 ++ crates/clock/src/clock.rs | 10 ++- crates/clock/src/system_clock.rs | 59 +++++++++++++ crates/collab/src/tests/test_server.rs | 4 +- .../src/chat_panel/message_editor.rs | 4 +- crates/project/src/project.rs | 5 +- crates/project/src/worktree_tests.rs | 20 ++++- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 3 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 3 +- 16 files changed, 213 insertions(+), 55 deletions(-) create mode 100644 crates/clock/src/system_clock.rs diff --git a/Cargo.lock b/Cargo.lock index 20d9ea6c51..af335bce67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1911,6 +1911,7 @@ dependencies = [ "async-recursion 0.3.2", "async-tungstenite", "chrono", + "clock", "collections", "db", "feature_flags", @@ -1948,6 +1949,8 @@ dependencies = [ name = "clock" version = "0.1.0" dependencies = [ + "chrono", + "parking_lot 0.11.2", "smallvec", ] @@ -11732,6 +11735,7 @@ dependencies = [ "bincode", "call", "client", + "clock", "collections", "db", "derive_more", @@ -11956,6 +11960,7 @@ dependencies = [ "chrono", "cli", "client", + "clock", "collab_ui", "collections", "command_palette", diff --git a/crates/channel/src/channel_store_tests.rs b/crates/channel/src/channel_store_tests.rs index c668b72022..fd76bcc301 100644 --- a/crates/channel/src/channel_store_tests.rs +++ b/crates/channel/src/channel_store_tests.rs @@ -2,6 +2,7 @@ use crate::channel_chat::ChannelChatEvent; use super::*; use client::{test::FakeServer, Client, UserStore}; +use clock::FakeSystemClock; use gpui::{AppContext, Context, Model, TestAppContext}; use rpc::proto::{self}; use settings::SettingsStore; @@ -337,8 +338,9 @@ fn init_test(cx: &mut AppContext) -> Model { release_channel::init("0.0.0", cx); client::init_settings(cx); + let clock = Arc::new(FakeSystemClock::default()); let http = FakeHttpClient::with_404_response(); - let client = Client::new(http.clone(), cx); + let client = Client::new(clock, http.clone(), cx); let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); client::init(&client, cx); diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 9770ee9b8e..6e3660f5f2 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -10,10 +10,11 @@ path = "src/client.rs" doctest = false [features] -test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"] +test-support = ["clock/test-support", "collections/test-support", "gpui/test-support", "rpc/test-support"] [dependencies] chrono = { version = "0.4", features = ["serde"] } +clock.workspace = true collections.workspace = true db.workspace = true gpui.workspace = true @@ -51,6 +52,7 @@ uuid.workspace = true url.workspace = true [dev-dependencies] +clock = { workspace = true, features = ["test-support"] } collections = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] } rpc = { workspace = true, features = ["test-support"] } diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index e454d3ddaf..04d431b150 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -10,6 +10,7 @@ use async_tungstenite::tungstenite::{ error::Error as WebsocketError, http::{Request, StatusCode}, }; +use clock::SystemClock; use collections::HashMap; use futures::{ channel::oneshot, future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, @@ -421,11 +422,15 @@ impl settings::Settings for TelemetrySettings { } impl Client { - pub fn new(http: Arc, cx: &mut AppContext) -> Arc { + pub fn new( + clock: Arc, + http: Arc, + cx: &mut AppContext, + ) -> Arc { let client = Arc::new(Self { id: AtomicU64::new(0), peer: Peer::new(0), - telemetry: Telemetry::new(http.clone(), cx), + telemetry: Telemetry::new(clock, http.clone(), cx), http, state: Default::default(), @@ -1455,6 +1460,7 @@ mod tests { use super::*; use crate::test::FakeServer; + use clock::FakeSystemClock; use gpui::{BackgroundExecutor, Context, TestAppContext}; use parking_lot::Mutex; use settings::SettingsStore; @@ -1465,7 +1471,13 @@ mod tests { async fn test_reconnection(cx: &mut TestAppContext) { init_test(cx); let user_id = 5; - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let server = FakeServer::for_client(user_id, &client, cx).await; let mut status = client.status(); assert!(matches!( @@ -1500,7 +1512,13 @@ mod tests { async fn test_connection_timeout(executor: BackgroundExecutor, cx: &mut TestAppContext) { init_test(cx); let user_id = 5; - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let mut status = client.status(); // Time out when client tries to connect. @@ -1573,7 +1591,13 @@ mod tests { init_test(cx); let auth_count = Arc::new(Mutex::new(0)); let dropped_auth_count = Arc::new(Mutex::new(0)); - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); client.override_authenticate({ let auth_count = auth_count.clone(); let dropped_auth_count = dropped_auth_count.clone(); @@ -1621,7 +1645,13 @@ mod tests { async fn test_subscribing_to_entity(cx: &mut TestAppContext) { init_test(cx); let user_id = 5; - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let server = FakeServer::for_client(user_id, &client, cx).await; let (done_tx1, mut done_rx1) = smol::channel::unbounded(); @@ -1675,7 +1705,13 @@ mod tests { async fn test_subscribing_after_dropping_subscription(cx: &mut TestAppContext) { init_test(cx); let user_id = 5; - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let server = FakeServer::for_client(user_id, &client, cx).await; let model = cx.new_model(|_| TestModel::default()); @@ -1704,7 +1740,13 @@ mod tests { async fn test_dropping_subscription_in_handler(cx: &mut TestAppContext) { init_test(cx); let user_id = 5; - let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let server = FakeServer::for_client(user_id, &client, cx).await; let model = cx.new_model(|_| TestModel::default()); diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 9bdf038b26..085ad2cb67 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -2,6 +2,7 @@ mod event_coalescer; use crate::TelemetrySettings; use chrono::{DateTime, Utc}; +use clock::SystemClock; use futures::Future; use gpui::{AppContext, AppMetadata, BackgroundExecutor, Task}; use once_cell::sync::Lazy; @@ -24,6 +25,7 @@ use util::TryFutureExt; use self::event_coalescer::EventCoalescer; pub struct Telemetry { + clock: Arc, http_client: Arc, executor: BackgroundExecutor, state: Arc>, @@ -156,7 +158,11 @@ static ZED_CLIENT_CHECKSUM_SEED: Lazy>> = Lazy::new(|| { }); impl Telemetry { - pub fn new(client: Arc, cx: &mut AppContext) -> Arc { + pub fn new( + clock: Arc, + client: Arc, + cx: &mut AppContext, + ) -> Arc { let release_channel = ReleaseChannel::try_global(cx).map(|release_channel| release_channel.display_name()); @@ -205,6 +211,7 @@ impl Telemetry { // TODO: Replace all hardware stuff with nested SystemSpecs json let this = Arc::new(Self { + clock, http_client: client, executor: cx.background_executor().clone(), state, @@ -317,7 +324,8 @@ impl Telemetry { operation, copilot_enabled, copilot_enabled_for_language, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -333,7 +341,8 @@ impl Telemetry { suggestion_id, suggestion_accepted, file_extension, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -349,7 +358,8 @@ impl Telemetry { conversation_id, kind, model, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -365,7 +375,8 @@ impl Telemetry { operation, room_id, channel_id, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -375,7 +386,8 @@ impl Telemetry { let event = Event::Cpu { usage_as_percentage, core_count, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -389,24 +401,18 @@ impl Telemetry { let event = Event::Memory { memory_in_bytes, virtual_memory_in_bytes, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) } - pub fn report_app_event(self: &Arc, operation: String) { - self.report_app_event_with_date_time(operation, Utc::now()); - } - - fn report_app_event_with_date_time( - self: &Arc, - operation: String, - date_time: DateTime, - ) -> Event { + pub fn report_app_event(self: &Arc, operation: String) -> Event { let event = Event::App { operation, - milliseconds_since_first_event: self.milliseconds_since_first_event(date_time), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event.clone()); @@ -418,7 +424,8 @@ impl Telemetry { let event = Event::Setting { setting, value, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -433,7 +440,8 @@ impl Telemetry { let event = Event::Edit { duration: end.timestamp_millis() - start.timestamp_millis(), environment, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event); @@ -444,7 +452,8 @@ impl Telemetry { let event = Event::Action { source, action, - milliseconds_since_first_event: self.milliseconds_since_first_event(Utc::now()), + milliseconds_since_first_event: self + .milliseconds_since_first_event(self.clock.utc_now()), }; self.report_event(event) @@ -590,29 +599,32 @@ impl Telemetry { mod tests { use super::*; use chrono::TimeZone; + use clock::FakeSystemClock; use gpui::TestAppContext; use util::http::FakeHttpClient; #[gpui::test] fn test_telemetry_flush_on_max_queue_size(cx: &mut TestAppContext) { init_test(cx); + let clock = Arc::new(FakeSystemClock::new( + Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(), + )); let http = FakeHttpClient::with_200_response(); let installation_id = Some("installation_id".to_string()); let session_id = "session_id".to_string(); cx.update(|cx| { - let telemetry = Telemetry::new(http, cx); + let telemetry = Telemetry::new(clock.clone(), http, cx); telemetry.state.lock().max_queue_size = 4; telemetry.start(installation_id, session_id, cx); assert!(is_empty_state(&telemetry)); - let first_date_time = Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(); + let first_date_time = clock.utc_now(); let operation = "test".to_string(); - let event = - telemetry.report_app_event_with_date_time(operation.clone(), first_date_time); + let event = telemetry.report_app_event(operation.clone()); assert_eq!( event, Event::App { @@ -627,9 +639,9 @@ mod tests { Some(first_date_time) ); - let mut date_time = first_date_time + chrono::Duration::milliseconds(100); + clock.advance(chrono::Duration::milliseconds(100)); - let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time); + let event = telemetry.report_app_event(operation.clone()); assert_eq!( event, Event::App { @@ -644,9 +656,9 @@ mod tests { Some(first_date_time) ); - date_time += chrono::Duration::milliseconds(100); + clock.advance(chrono::Duration::milliseconds(100)); - let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time); + let event = telemetry.report_app_event(operation.clone()); assert_eq!( event, Event::App { @@ -661,10 +673,10 @@ mod tests { Some(first_date_time) ); - date_time += chrono::Duration::milliseconds(100); + clock.advance(chrono::Duration::milliseconds(100)); // Adding a 4th event should cause a flush - let event = telemetry.report_app_event_with_date_time(operation.clone(), date_time); + let event = telemetry.report_app_event(operation.clone()); assert_eq!( event, Event::App { @@ -680,22 +692,24 @@ mod tests { #[gpui::test] async fn test_connection_timeout(executor: BackgroundExecutor, cx: &mut TestAppContext) { init_test(cx); + let clock = Arc::new(FakeSystemClock::new( + Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(), + )); let http = FakeHttpClient::with_200_response(); let installation_id = Some("installation_id".to_string()); let session_id = "session_id".to_string(); cx.update(|cx| { - let telemetry = Telemetry::new(http, cx); + let telemetry = Telemetry::new(clock.clone(), http, cx); telemetry.state.lock().max_queue_size = 4; telemetry.start(installation_id, session_id, cx); assert!(is_empty_state(&telemetry)); - let first_date_time = Utc.with_ymd_and_hms(1990, 4, 12, 12, 0, 0).unwrap(); + let first_date_time = clock.utc_now(); let operation = "test".to_string(); - let event = - telemetry.report_app_event_with_date_time(operation.clone(), first_date_time); + let event = telemetry.report_app_event(operation.clone()); assert_eq!( event, Event::App { diff --git a/crates/clock/Cargo.toml b/crates/clock/Cargo.toml index c81a6fda67..adef338cc7 100644 --- a/crates/clock/Cargo.toml +++ b/crates/clock/Cargo.toml @@ -9,5 +9,10 @@ license = "GPL-3.0-or-later" path = "src/clock.rs" doctest = false +[features] +test-support = ["dep:parking_lot"] + [dependencies] +chrono.workspace = true +parking_lot = { workspace = true, optional = true } smallvec.workspace = true diff --git a/crates/clock/src/clock.rs b/crates/clock/src/clock.rs index a781cf2d44..7a1377981a 100644 --- a/crates/clock/src/clock.rs +++ b/crates/clock/src/clock.rs @@ -1,13 +1,17 @@ +mod system_clock; + use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, fmt, iter, }; -/// A unique identifier for each distributed node +pub use system_clock::*; + +/// A unique identifier for each distributed node. pub type ReplicaId = u16; -/// A [Lamport sequence number](https://en.wikipedia.org/wiki/Lamport_timestamp), +/// A [Lamport sequence number](https://en.wikipedia.org/wiki/Lamport_timestamp). pub type Seq = u32; /// A [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp), @@ -18,7 +22,7 @@ pub struct Lamport { pub value: Seq, } -/// A [vector clock](https://en.wikipedia.org/wiki/Vector_clock) +/// A [vector clock](https://en.wikipedia.org/wiki/Vector_clock). #[derive(Clone, Default, Hash, Eq, PartialEq)] pub struct Global(SmallVec<[u32; 8]>); diff --git a/crates/clock/src/system_clock.rs b/crates/clock/src/system_clock.rs new file mode 100644 index 0000000000..a462ffc35b --- /dev/null +++ b/crates/clock/src/system_clock.rs @@ -0,0 +1,59 @@ +use chrono::{DateTime, Utc}; + +pub trait SystemClock: Send + Sync { + /// Returns the current date and time in UTC. + fn utc_now(&self) -> DateTime; +} + +pub struct RealSystemClock; + +impl SystemClock for RealSystemClock { + fn utc_now(&self) -> DateTime { + Utc::now() + } +} + +#[cfg(any(test, feature = "test-support"))] +pub struct FakeSystemClockState { + now: DateTime, +} + +#[cfg(any(test, feature = "test-support"))] +pub struct FakeSystemClock { + // Use an unfair lock to ensure tests are deterministic. + state: parking_lot::Mutex, +} + +#[cfg(any(test, feature = "test-support"))] +impl Default for FakeSystemClock { + fn default() -> Self { + Self::new(Utc::now()) + } +} + +#[cfg(any(test, feature = "test-support"))] +impl FakeSystemClock { + pub fn new(now: DateTime) -> Self { + let state = FakeSystemClockState { now }; + + Self { + state: parking_lot::Mutex::new(state), + } + } + + pub fn set_now(&self, now: DateTime) { + self.state.lock().now = now; + } + + /// Advances the [`FakeSystemClock`] by the specified [`Duration`](chrono::Duration). + pub fn advance(&self, duration: chrono::Duration) { + self.state.lock().now += duration; + } +} + +#[cfg(any(test, feature = "test-support"))] +impl SystemClock for FakeSystemClock { + fn utc_now(&self) -> DateTime { + self.state.lock().now + } +} diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index c12631761d..56705f182d 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -10,6 +10,7 @@ use channel::{ChannelBuffer, ChannelStore}; use client::{ self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore, }; +use clock::FakeSystemClock; use collab_ui::channel_view::ChannelView; use collections::{HashMap, HashSet}; use fs::FakeFs; @@ -163,6 +164,7 @@ impl TestServer { client::init_settings(cx); }); + let clock = Arc::new(FakeSystemClock::default()); let http = FakeHttpClient::with_404_response(); let user_id = if let Ok(Some(user)) = self.app_state.db.get_user_by_github_login(name).await { @@ -185,7 +187,7 @@ impl TestServer { .user_id }; let client_name = name.to_string(); - let mut client = cx.update(|cx| Client::new(http.clone(), cx)); + let mut client = cx.update(|cx| Client::new(clock, http.clone(), cx)); let server = self.server.clone(); let db = self.app_state.db.clone(); let connection_killers = self.connection_killers.clone(); diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index ed47c7a54a..69e6c78735 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -385,6 +385,7 @@ impl Render for MessageEditor { mod tests { use super::*; use client::{Client, User, UserStore}; + use clock::FakeSystemClock; use gpui::TestAppContext; use language::{Language, LanguageConfig}; use rpc::proto; @@ -455,8 +456,9 @@ mod tests { let settings = SettingsStore::test(cx); cx.set_global(settings); + let clock = Arc::new(FakeSystemClock::default()); let http = FakeHttpClient::with_404_response(); - let client = Client::new(http.clone(), cx); + let client = Client::new(clock, http.clone(), cx); let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); theme::init(theme::LoadThemes::JustBase, cx); language::init(cx); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 952a1bf378..0c82a40c16 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -853,10 +853,13 @@ impl Project { root_paths: impl IntoIterator, cx: &mut gpui::TestAppContext, ) -> Model { + use clock::FakeSystemClock; + let mut languages = LanguageRegistry::test(); languages.set_executor(cx.executor()); + let clock = Arc::new(FakeSystemClock::default()); let http_client = util::http::FakeHttpClient::with_404_response(); - let client = cx.update(|cx| client::Client::new(http_client.clone(), cx)); + let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx)); let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); let project = cx.update(|cx| { Project::local( diff --git a/crates/project/src/worktree_tests.rs b/crates/project/src/worktree_tests.rs index e3725d43d5..1ee7e2cdab 100644 --- a/crates/project/src/worktree_tests.rs +++ b/crates/project/src/worktree_tests.rs @@ -5,6 +5,7 @@ use crate::{ }; use anyhow::Result; use client::Client; +use clock::FakeSystemClock; use fs::{repository::GitFileStatus, FakeFs, Fs, RealFs, RemoveOptions}; use git::GITIGNORE; use gpui::{ModelContext, Task, TestAppContext}; @@ -1263,7 +1264,13 @@ async fn test_create_directory_during_initial_scan(cx: &mut TestAppContext) { async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); - let client_fake = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client_fake = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let fs_fake = FakeFs::new(cx.background_executor.clone()); fs_fake @@ -1304,7 +1311,13 @@ async fn test_create_dir_all_on_create_entry(cx: &mut TestAppContext) { assert!(tree.entry_for_path("a/b/").unwrap().is_dir()); }); - let client_real = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let client_real = cx.update(|cx| { + Client::new( + Arc::new(FakeSystemClock::default()), + FakeHttpClient::with_404_response(), + cx, + ) + }); let fs_real = Arc::new(RealFs); let temp_root = temp_tree(json!({ @@ -2396,8 +2409,9 @@ async fn test_propagate_git_statuses(cx: &mut TestAppContext) { } fn build_client(cx: &mut TestAppContext) -> Arc { + let clock = Arc::new(FakeSystemClock::default()); let http_client = FakeHttpClient::with_404_response(); - cx.update(|cx| Client::new(http_client, cx)) + cx.update(|cx| Client::new(clock, http_client, cx)) } #[track_caller] diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 8d82d29c20..c7cb203d4c 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -25,6 +25,7 @@ async-recursion = "1.0.0" bincode = "1.2.1" call.workspace = true client.workspace = true +clock.workspace = true collections.workspace = true db.workspace = true derive_more.workspace = true diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index da9512b42a..77b99f76c5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -397,8 +397,9 @@ impl AppState { let fs = fs::FakeFs::new(cx.background_executor().clone()); let languages = Arc::new(LanguageRegistry::test()); + let clock = Arc::new(clock::FakeSystemClock::default()); let http_client = util::http::FakeHttpClient::with_404_response(); - let client = Client::new(http_client.clone(), cx); + let client = Client::new(clock, http_client.clone(), cx); let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx)); let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx)); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 74e010b152..dd0da9f4cf 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -34,6 +34,7 @@ channel.workspace = true chrono = "0.4" cli.workspace = true client.workspace = true +clock.workspace = true collab_ui.workspace = true collections.workspace = true command_palette.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 41ed1d2d25..25e0980f90 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -140,9 +140,10 @@ fn main() { handle_keymap_file_changes(user_keymap_file_rx, cx); client::init_settings(cx); + let clock = Arc::new(clock::RealSystemClock); let http = http::zed_client(&client::ClientSettings::get_global(cx).server_url); - let client = client::Client::new(http.clone(), cx); + let client = client::Client::new(clock, http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); let copilot_language_server_id = languages.next_language_server_id(); languages.set_executor(cx.background_executor().clone());