Skip to content

Commit 7522b98

Browse files
committed
More integration tests
1 parent e4ba975 commit 7522b98

20 files changed

+885
-105
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_typos.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ poit = "poit"
1313
BA = "BA" # acronym for "Bad Allowed", used in testing.
1414
jod = "jod" # e.g., `jod-thread`
1515
Numer = "Numer" # Library name 'NumerBlox' in "Who's Using Ruff?"
16+
"FrIeAndLy-._.-bArD" = "FrIeAndLy-._.-bArD"
1617

1718
[default]
1819
extend-ignore-re = [

crates/red_knot/tests/file_watching.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::time::Duration;
66
use anyhow::{anyhow, Context};
77

88
use red_knot_python_semantic::{resolve_module, ModuleName, Program, PythonVersion, SitePackages};
9-
use red_knot_workspace::db::RootDatabase;
9+
use red_knot_workspace::db::{Db, RootDatabase};
1010
use red_knot_workspace::watch;
1111
use red_knot_workspace::watch::{directory_watcher, WorkspaceWatcher};
1212
use red_knot_workspace::workspace::settings::{Configuration, SearchPathConfiguration};
@@ -1310,3 +1310,42 @@ mod unix {
13101310
Ok(())
13111311
}
13121312
}
1313+
1314+
#[test]
1315+
fn nested_packages_delete_root() -> anyhow::Result<()> {
1316+
let mut case = setup(|root: &SystemPath, workspace_root: &SystemPath| {
1317+
std::fs::write(
1318+
workspace_root.join("pyproject.toml").as_std_path(),
1319+
r#"
1320+
[project]
1321+
name = "inner"
1322+
"#,
1323+
)?;
1324+
1325+
std::fs::write(
1326+
root.join("pyproject.toml").as_std_path(),
1327+
r#"
1328+
[project]
1329+
name = "outer"
1330+
"#,
1331+
)?;
1332+
1333+
Ok(())
1334+
})?;
1335+
1336+
assert_eq!(
1337+
case.db().workspace().root(case.db()),
1338+
&*case.workspace_path("")
1339+
);
1340+
1341+
std::fs::remove_file(case.workspace_path("pyproject.toml").as_std_path())?;
1342+
1343+
let changes = case.stop_watch();
1344+
1345+
case.apply_changes(changes);
1346+
1347+
// It should now pick up the outer workspace.
1348+
assert_eq!(case.db().workspace().root(case.db()), case.root_path());
1349+
1350+
Ok(())
1351+
}

crates/red_knot_wasm/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::any::Any;
33
use js_sys::Error;
44
use wasm_bindgen::prelude::*;
55

6-
use red_knot_workspace::db::RootDatabase;
6+
use red_knot_workspace::db::{Db, RootDatabase};
77
use red_knot_workspace::workspace::settings::Configuration;
88
use red_knot_workspace::workspace::WorkspaceMetadata;
99
use ruff_db::diagnostic::Diagnostic;

crates/red_knot_workspace/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ tracing = { workspace = true }
3535

3636
[dev-dependencies]
3737
ruff_db = { workspace = true, features = ["testing"] }
38+
insta = { workspace = true }
3839
tempfile = { workspace = true }
3940

4041
[features]

crates/red_knot_workspace/src/db.rs

+14-29
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use ruff_db::{Db as SourceDb, Upcast};
1515
mod changes;
1616

1717
#[salsa::db]
18-
pub trait Db: SemanticDb + Upcast<dyn SemanticDb> {}
18+
pub trait Db: SemanticDb + Upcast<dyn SemanticDb> {
19+
fn workspace(&self) -> Workspace;
20+
}
1921

2022
#[salsa::db]
2123
pub struct RootDatabase {
@@ -38,39 +40,13 @@ impl RootDatabase {
3840
};
3941

4042
// Initialize the `Program` singleton
41-
42-
// TODO: Reasoning: We need to know the target version at this point to
43-
// load the correct persistent cache so that running knot with
44-
// different target versions doesn't result in 0 cache-reuse.
45-
// But this does complicate things a bit because we need to have the
46-
// resolved workspace settings at this point.
47-
// But resolving the settings fits well into the workspace's responsibility.
48-
// Should `Workspace` initialize the Program -> No, because we then need to create
49-
// the program first.
50-
51-
// Metadata: Plain description of the workspace and the exact configuration (without doing any resolution)
52-
// Workspace: Resolved workspace with all members, merged settings.
53-
// Middle ground -> `metadata.resolve_settings(configuration)` returns the resolved
54-
// `.workspace` configuration but it doesn't resolve any of the members configuration.
55-
// The problem with this is that it makes the single project use case awkward. We should just
56-
// resolve the program settings and defer everything else to later? But how does this work with
57-
// with target versions that are inherited from the workspace?
58-
// So this is somewhat awkward too :(
59-
// The other option is that `WorkspaceMetadata` keeps working as it is today where it stores the
60-
// resolved settings instead of the unresolved. I do thik that this would require another intermediate representation
61-
// to not be awkward.
6243
Program::from_settings(&db, workspace.settings().program())?;
6344

6445
db.workspace = Some(Workspace::from_metadata(&db, workspace));
6546

6647
Ok(db)
6748
}
6849

69-
pub fn workspace(&self) -> Workspace {
70-
// SAFETY: The workspace is always initialized in `new`.
71-
self.workspace.unwrap()
72-
}
73-
7450
/// Checks all open files in the workspace and its dependencies.
7551
pub fn check(&self) -> Result<Vec<Box<dyn Diagnostic>>, Cancelled> {
7652
self.with_db(|db| db.workspace().check(db))
@@ -174,7 +150,11 @@ impl salsa::Database for RootDatabase {
174150
}
175151

176152
#[salsa::db]
177-
impl Db for RootDatabase {}
153+
impl Db for RootDatabase {
154+
fn workspace(&self) -> Workspace {
155+
self.workspace.unwrap()
156+
}
157+
}
178158

179159
#[cfg(test)]
180160
pub(crate) mod tests {
@@ -189,6 +169,7 @@ pub(crate) mod tests {
189169
use ruff_db::{Db as SourceDb, Upcast};
190170

191171
use crate::db::Db;
172+
use crate::workspace::Workspace;
192173

193174
#[salsa::db]
194175
pub(crate) struct TestDb {
@@ -275,7 +256,11 @@ pub(crate) mod tests {
275256
}
276257

277258
#[salsa::db]
278-
impl Db for TestDb {}
259+
impl Db for TestDb {
260+
fn workspace(&self) -> Workspace {
261+
todo!()
262+
}
263+
}
279264

280265
#[salsa::db]
281266
impl salsa::Database for TestDb {

crates/red_knot_workspace/src/db/changes.rs

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
1+
use crate::db::{Db, RootDatabase};
2+
use crate::watch;
3+
use crate::watch::{CreatedKind, DeletedKind};
4+
use crate::workspace::settings::Configuration;
5+
use crate::workspace::{Workspace, WorkspaceMetadata};
16
use red_knot_python_semantic::Program;
27
use ruff_db::files::{system_path_to_file, File, Files};
38
use ruff_db::system::walk_directory::WalkState;
49
use ruff_db::system::SystemPath;
5-
use ruff_db::Db;
10+
use ruff_db::Db as _;
611
use rustc_hash::FxHashSet;
712

8-
use crate::db::RootDatabase;
9-
use crate::watch;
10-
use crate::watch::{CreatedKind, DeletedKind};
11-
use crate::workspace::settings::Configuration;
12-
use crate::workspace::WorkspaceMetadata;
13-
1413
impl RootDatabase {
1514
#[tracing::instrument(level = "debug", skip(self, changes, base_configuration))]
1615
pub fn apply_changes(
1716
&mut self,
1817
changes: Vec<watch::ChangeEvent>,
1918
base_configuration: Option<&Configuration>,
2019
) {
21-
let workspace = self.workspace();
20+
let mut workspace = self.workspace();
2221
let workspace_path = workspace.root(self).to_path_buf();
2322
let program = Program::get(self);
2423
let custom_stdlib_versions_path = program
@@ -58,6 +57,10 @@ impl RootDatabase {
5857
// Changes to ignore files or settings can change the workspace structure or add/remove files
5958
// from packages.
6059
if let Some(package) = workspace.package(self, path) {
60+
if package.root(self) == workspace.root(self) {
61+
workspace_change = true;
62+
}
63+
6164
changed_packages.insert(package);
6265
} else {
6366
workspace_change = true;
@@ -153,12 +156,20 @@ impl RootDatabase {
153156
if workspace_change {
154157
match WorkspaceMetadata::discover(&workspace_path, self.system(), base_configuration) {
155158
Ok(metadata) => {
156-
tracing::debug!("Reloading workspace after structural change");
157-
// TODO: Handle changes in the program settings.
158-
workspace.reload(self, metadata);
159+
if metadata.root() == workspace.root(self) {
160+
tracing::debug!("Reloading workspace after structural change");
161+
// TODO: Handle changes in the program settings.
162+
workspace.reload(self, metadata);
163+
} else {
164+
tracing::debug!("Replace workspace after structural change");
165+
workspace = Workspace::from_metadata(self, metadata);
166+
self.workspace = Some(workspace);
167+
}
159168
}
160169
Err(error) => {
161-
tracing::error!("Failed to load workspace, keep old workspace: {error}");
170+
tracing::error!(
171+
"Failed to load workspace, keeping old workspace configuration: {error}"
172+
);
162173
}
163174
}
164175

@@ -223,6 +234,3 @@ impl RootDatabase {
223234
}
224235
}
225236
}
226-
227-
#[cfg(test)]
228-
mod tests {}

crates/red_knot_workspace/src/watch/workspace_watcher.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use ruff_cache::{CacheKey, CacheKeyHasher};
88
use ruff_db::system::{SystemPath, SystemPathBuf};
99
use ruff_db::Upcast;
1010

11-
use crate::db::RootDatabase;
11+
use crate::db::{Db, RootDatabase};
1212
use crate::watch::Watcher;
1313

1414
/// Wrapper around a [`Watcher`] that watches the relevant paths of a workspace.

0 commit comments

Comments
 (0)