Skip to content

Commit 118ae5b

Browse files
committed
Store user configuration override on OsSystem
1 parent 783f3a6 commit 118ae5b

File tree

3 files changed

+156
-103
lines changed

3 files changed

+156
-103
lines changed

crates/red_knot/tests/file_watching.rs

Lines changed: 107 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use red_knot_project::{Db, ProjectDatabase, ProjectMetadata};
1212
use red_knot_python_semantic::{resolve_module, ModuleName, PythonPlatform, PythonVersion};
1313
use ruff_db::files::{system_path_to_file, File, FileError};
1414
use ruff_db::source::source_text;
15-
use ruff_db::system::{OsSystem, OsUserDirectoryOverride, SystemPath, SystemPathBuf};
15+
use ruff_db::system::{
16+
OsSystem, System, SystemPath, SystemPathBuf, UserConfigDirectoryOverrideGuard,
17+
};
1618
use ruff_db::Upcast;
1719

1820
struct TestCase {
@@ -220,17 +222,44 @@ where
220222
}
221223

222224
trait SetupFiles {
223-
fn setup(self, root_path: &SystemPath, project_path: &SystemPath) -> anyhow::Result<()>;
225+
fn setup(self, context: &SetupContext) -> anyhow::Result<()>;
226+
}
227+
228+
struct SetupContext<'a> {
229+
system: &'a OsSystem,
230+
root_path: &'a SystemPath,
231+
}
232+
233+
impl<'a> SetupContext<'a> {
234+
fn system(&self) -> &'a OsSystem {
235+
self.system
236+
}
237+
238+
fn join_project_path(&self, relative: impl AsRef<SystemPath>) -> SystemPathBuf {
239+
self.project_path().join(relative)
240+
}
241+
242+
fn project_path(&self) -> &SystemPath {
243+
self.system.current_directory()
244+
}
245+
246+
fn root_path(&self) -> &'a SystemPath {
247+
self.root_path
248+
}
249+
250+
fn join_root_path(&self, relative: impl AsRef<SystemPath>) -> SystemPathBuf {
251+
self.root_path().join(relative)
252+
}
224253
}
225254

226255
impl<const N: usize, P> SetupFiles for [(P, &'static str); N]
227256
where
228257
P: AsRef<SystemPath>,
229258
{
230-
fn setup(self, _root_path: &SystemPath, project_path: &SystemPath) -> anyhow::Result<()> {
259+
fn setup(self, context: &SetupContext) -> anyhow::Result<()> {
231260
for (relative_path, content) in self {
232261
let relative_path = relative_path.as_ref();
233-
let absolute_path = project_path.join(relative_path);
262+
let absolute_path = context.join_project_path(relative_path);
234263
if let Some(parent) = absolute_path.parent() {
235264
std::fs::create_dir_all(parent).with_context(|| {
236265
format!("Failed to create parent directory for file `{relative_path}`")
@@ -250,24 +279,23 @@ where
250279

251280
impl<F> SetupFiles for F
252281
where
253-
F: FnOnce(&SystemPath, &SystemPath) -> anyhow::Result<()>,
282+
F: FnOnce(&SetupContext) -> anyhow::Result<()>,
254283
{
255-
fn setup(self, root_path: &SystemPath, project_path: &SystemPath) -> anyhow::Result<()> {
256-
self(root_path, project_path)
284+
fn setup(self, context: &SetupContext) -> anyhow::Result<()> {
285+
self(context)
257286
}
258287
}
259288

260289
fn setup<F>(setup_files: F) -> anyhow::Result<TestCase>
261290
where
262291
F: SetupFiles,
263292
{
264-
setup_with_options(setup_files, |_root, _project_path| None)
293+
setup_with_options(setup_files, |_context| None)
265294
}
266295

267-
// TODO: Replace with configuration?
268296
fn setup_with_options<F>(
269297
setup_files: F,
270-
create_options: impl FnOnce(&SystemPath, &SystemPath) -> Option<Options>,
298+
create_options: impl FnOnce(&SetupContext) -> Option<Options>,
271299
) -> anyhow::Result<TestCase>
272300
where
273301
F: SetupFiles,
@@ -295,13 +323,17 @@ where
295323
std::fs::create_dir_all(project_path.as_std_path())
296324
.with_context(|| format!("Failed to create project directory `{project_path}`"))?;
297325

326+
let system = OsSystem::new(&project_path);
327+
let setup_context = SetupContext {
328+
system: &system,
329+
root_path: &root_path,
330+
};
331+
298332
setup_files
299-
.setup(&root_path, &project_path)
333+
.setup(&setup_context)
300334
.context("Failed to setup test files")?;
301335

302-
let system = OsSystem::new(&project_path);
303-
304-
if let Some(options) = create_options(&root_path, &project_path) {
336+
if let Some(options) = create_options(&setup_context) {
305337
std::fs::write(
306338
project_path.join("pyproject.toml").as_std_path(),
307339
toml::to_string(&PyProject {
@@ -791,10 +823,12 @@ fn directory_deleted() -> anyhow::Result<()> {
791823

792824
#[test]
793825
fn search_path() -> anyhow::Result<()> {
794-
let mut case = setup_with_options([("bar.py", "import sub.a")], |root_path, _project_path| {
826+
let mut case = setup_with_options([("bar.py", "import sub.a")], |context| {
795827
Some(Options {
796828
environment: Some(EnvironmentOptions {
797-
extra_paths: Some(vec![RelativePathBuf::cli(root_path.join("site_packages"))]),
829+
extra_paths: Some(vec![RelativePathBuf::cli(
830+
context.join_root_path("site_packages"),
831+
)]),
798832
..EnvironmentOptions::default()
799833
}),
800834
..Options::default()
@@ -855,10 +889,12 @@ fn add_search_path() -> anyhow::Result<()> {
855889

856890
#[test]
857891
fn remove_search_path() -> anyhow::Result<()> {
858-
let mut case = setup_with_options([("bar.py", "import sub.a")], |root_path, _project_path| {
892+
let mut case = setup_with_options([("bar.py", "import sub.a")], |context| {
859893
Some(Options {
860894
environment: Some(EnvironmentOptions {
861-
extra_paths: Some(vec![RelativePathBuf::cli(root_path.join("site_packages"))]),
895+
extra_paths: Some(vec![RelativePathBuf::cli(
896+
context.join_root_path("site_packages"),
897+
)]),
862898
..EnvironmentOptions::default()
863899
}),
864900
..Options::default()
@@ -896,7 +932,7 @@ import os
896932
print(sys.last_exc, os.getegid())
897933
"#,
898934
)],
899-
|_root_path, _project_path| {
935+
|_context| {
900936
Some(Options {
901937
environment: Some(EnvironmentOptions {
902938
python_version: Some(RangedValue::cli(PythonVersion::PY311)),
@@ -944,21 +980,31 @@ print(sys.last_exc, os.getegid())
944980
#[test]
945981
fn changed_versions_file() -> anyhow::Result<()> {
946982
let mut case = setup_with_options(
947-
|root_path: &SystemPath, project_path: &SystemPath| {
948-
std::fs::write(project_path.join("bar.py").as_std_path(), "import sub.a")?;
949-
std::fs::create_dir_all(root_path.join("typeshed/stdlib").as_std_path())?;
950-
std::fs::write(root_path.join("typeshed/stdlib/VERSIONS").as_std_path(), "")?;
983+
|context: &SetupContext| {
984+
std::fs::write(
985+
context.join_project_path("bar.py").as_std_path(),
986+
"import sub.a",
987+
)?;
988+
std::fs::create_dir_all(context.join_root_path("typeshed/stdlib").as_std_path())?;
989+
std::fs::write(
990+
context
991+
.join_root_path("typeshed/stdlib/VERSIONS")
992+
.as_std_path(),
993+
"",
994+
)?;
951995
std::fs::write(
952-
root_path.join("typeshed/stdlib/os.pyi").as_std_path(),
996+
context
997+
.join_root_path("typeshed/stdlib/os.pyi")
998+
.as_std_path(),
953999
"# not important",
9541000
)?;
9551001

9561002
Ok(())
9571003
},
958-
|root_path, _project_path| {
1004+
|context| {
9591005
Some(Options {
9601006
environment: Some(EnvironmentOptions {
961-
typeshed: Some(RelativePathBuf::cli(root_path.join("typeshed"))),
1007+
typeshed: Some(RelativePathBuf::cli(context.join_root_path("typeshed"))),
9621008
..EnvironmentOptions::default()
9631009
}),
9641010
..Options::default()
@@ -1009,12 +1055,12 @@ fn changed_versions_file() -> anyhow::Result<()> {
10091055
/// we're seeing is that Windows only emits a single event, similar to Linux.
10101056
#[test]
10111057
fn hard_links_in_project() -> anyhow::Result<()> {
1012-
let mut case = setup(|_root: &SystemPath, project: &SystemPath| {
1013-
let foo_path = project.join("foo.py");
1058+
let mut case = setup(|context: &SetupContext| {
1059+
let foo_path = context.join_project_path("foo.py");
10141060
std::fs::write(foo_path.as_std_path(), "print('Version 1')")?;
10151061

10161062
// Create a hardlink to `foo`
1017-
let bar_path = project.join("bar.py");
1063+
let bar_path = context.join_project_path("bar.py");
10181064
std::fs::hard_link(foo_path.as_std_path(), bar_path.as_std_path())
10191065
.context("Failed to create hard link from foo.py -> bar.py")?;
10201066

@@ -1080,12 +1126,12 @@ fn hard_links_in_project() -> anyhow::Result<()> {
10801126
ignore = "windows doesn't support observing changes to hard linked files."
10811127
)]
10821128
fn hard_links_to_target_outside_project() -> anyhow::Result<()> {
1083-
let mut case = setup(|root: &SystemPath, project: &SystemPath| {
1084-
let foo_path = root.join("foo.py");
1129+
let mut case = setup(|context: &SetupContext| {
1130+
let foo_path = context.join_root_path("foo.py");
10851131
std::fs::write(foo_path.as_std_path(), "print('Version 1')")?;
10861132

10871133
// Create a hardlink to `foo`
1088-
let bar_path = project.join("bar.py");
1134+
let bar_path = context.join_project_path("bar.py");
10891135
std::fs::hard_link(foo_path.as_std_path(), bar_path.as_std_path())
10901136
.context("Failed to create hard link from foo.py -> bar.py")?;
10911137

@@ -1188,17 +1234,17 @@ mod unix {
11881234
ignore = "FSEvents doesn't emit change events for symlinked directories outside of the watched paths."
11891235
)]
11901236
fn symlink_target_outside_watched_paths() -> anyhow::Result<()> {
1191-
let mut case = setup(|root: &SystemPath, project: &SystemPath| {
1237+
let mut case = setup(|context: &SetupContext| {
11921238
// Set up the symlink target.
1193-
let link_target = root.join("bar");
1239+
let link_target = context.join_root_path("bar");
11941240
std::fs::create_dir_all(link_target.as_std_path())
11951241
.context("Failed to create link target directory")?;
11961242
let baz_original = link_target.join("baz.py");
11971243
std::fs::write(baz_original.as_std_path(), "def baz(): ...")
11981244
.context("Failed to write link target file")?;
11991245

12001246
// Create a symlink inside the project
1201-
let bar = project.join("bar");
1247+
let bar = context.join_project_path("bar");
12021248
std::os::unix::fs::symlink(link_target.as_std_path(), bar.as_std_path())
12031249
.context("Failed to create symlink to bar package")?;
12041250

@@ -1269,17 +1315,17 @@ mod unix {
12691315
/// ```
12701316
#[test]
12711317
fn symlink_inside_project() -> anyhow::Result<()> {
1272-
let mut case = setup(|_root: &SystemPath, project: &SystemPath| {
1318+
let mut case = setup(|context: &SetupContext| {
12731319
// Set up the symlink target.
1274-
let link_target = project.join("patched/bar");
1320+
let link_target = context.join_project_path("patched/bar");
12751321
std::fs::create_dir_all(link_target.as_std_path())
12761322
.context("Failed to create link target directory")?;
12771323
let baz_original = link_target.join("baz.py");
12781324
std::fs::write(baz_original.as_std_path(), "def baz(): ...")
12791325
.context("Failed to write link target file")?;
12801326

12811327
// Create a symlink inside site-packages
1282-
let bar_in_project = project.join("bar");
1328+
let bar_in_project = context.join_project_path("bar");
12831329
std::os::unix::fs::symlink(link_target.as_std_path(), bar_in_project.as_std_path())
12841330
.context("Failed to create symlink to bar package")?;
12851331

@@ -1360,9 +1406,9 @@ mod unix {
13601406
#[test]
13611407
fn symlinked_module_search_path() -> anyhow::Result<()> {
13621408
let mut case = setup_with_options(
1363-
|root: &SystemPath, project: &SystemPath| {
1409+
|context: &SetupContext| {
13641410
// Set up the symlink target.
1365-
let site_packages = root.join("site-packages");
1411+
let site_packages = context.join_root_path("site-packages");
13661412
let bar = site_packages.join("bar");
13671413
std::fs::create_dir_all(bar.as_std_path())
13681414
.context("Failed to create bar directory")?;
@@ -1371,7 +1417,8 @@ mod unix {
13711417
.context("Failed to write baz.py")?;
13721418

13731419
// Symlink the site packages in the venv to the global site packages
1374-
let venv_site_packages = project.join(".venv/lib/python3.12/site-packages");
1420+
let venv_site_packages =
1421+
context.join_project_path(".venv/lib/python3.12/site-packages");
13751422
std::fs::create_dir_all(venv_site_packages.parent().unwrap())
13761423
.context("Failed to create .venv directory")?;
13771424
std::os::unix::fs::symlink(
@@ -1382,7 +1429,7 @@ mod unix {
13821429

13831430
Ok(())
13841431
},
1385-
|_root, _project| {
1432+
|_context| {
13861433
Some(Options {
13871434
environment: Some(EnvironmentOptions {
13881435
extra_paths: Some(vec![RelativePathBuf::cli(
@@ -1452,9 +1499,9 @@ mod unix {
14521499

14531500
#[test]
14541501
fn nested_projects_delete_root() -> anyhow::Result<()> {
1455-
let mut case = setup(|root: &SystemPath, project_root: &SystemPath| {
1502+
let mut case = setup(|context: &SetupContext| {
14561503
std::fs::write(
1457-
project_root.join("pyproject.toml").as_std_path(),
1504+
context.join_project_path("pyproject.toml").as_std_path(),
14581505
r#"
14591506
[project]
14601507
name = "inner"
@@ -1464,7 +1511,7 @@ fn nested_projects_delete_root() -> anyhow::Result<()> {
14641511
)?;
14651512

14661513
std::fs::write(
1467-
root.join("pyproject.toml").as_std_path(),
1514+
context.join_root_path("pyproject.toml").as_std_path(),
14681515
r#"
14691516
[project]
14701517
name = "outer"
@@ -1492,31 +1539,37 @@ fn nested_projects_delete_root() -> anyhow::Result<()> {
14921539

14931540
#[test]
14941541
fn changes_to_user_configuration() -> anyhow::Result<()> {
1495-
let mut _config_dir_override: Option<OsUserDirectoryOverride> = None;
1542+
let mut _config_dir_override: Option<UserConfigDirectoryOverrideGuard> = None;
14961543

1497-
let mut case = setup(|root: &SystemPath, project_root: &SystemPath| {
1544+
let mut case = setup(|context: &SetupContext| {
14981545
std::fs::write(
1499-
project_root.join("pyproject.toml").as_std_path(),
1546+
context.join_project_path("pyproject.toml").as_std_path(),
15001547
r#"
15011548
[project]
15021549
name = "test"
15031550
"#,
15041551
)?;
15051552

1506-
std::fs::write(project_root.join("foo.py").as_std_path(), "a = 10 / 0")?;
1553+
std::fs::write(
1554+
context.join_project_path("foo.py").as_std_path(),
1555+
"a = 10 / 0",
1556+
)?;
15071557

1508-
std::fs::create_dir_all(root.join("home/.config/knot").as_std_path())?;
1558+
let config_directory = context.join_root_path("home/.config");
1559+
std::fs::create_dir_all(config_directory.join("knot").as_std_path())?;
15091560
std::fs::write(
1510-
root.join("home/.config/knot/knot.toml").as_std_path(),
1561+
config_directory.join("knot/knot.toml").as_std_path(),
15111562
r#"
15121563
[rules]
15131564
division-by-zero = "ignore"
15141565
"#,
15151566
)?;
15161567

1517-
_config_dir_override = Some(OsUserDirectoryOverride::scoped(Some(
1518-
root.join("home/.config"),
1519-
)));
1568+
_config_dir_override = Some(
1569+
context
1570+
.system()
1571+
.with_user_config_directory(Some(config_directory)),
1572+
);
15201573

15211574
Ok(())
15221575
})?;

crates/ruff_db/src/system.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pub use glob::PatternError;
22
pub use memory_fs::MemoryFileSystem;
33

44
#[cfg(all(feature = "testing", feature = "os"))]
5-
pub use os::testing::OsUserDirectoryOverride;
5+
pub use os::testing::UserConfigDirectoryOverrideGuard;
66

77
#[cfg(feature = "os")]
88
pub use os::OsSystem;

0 commit comments

Comments
 (0)