Skip to content

Commit bd27bfa

Browse files
authored
[red-knot] Unify setup_db() functions, add TestDb builder (#14777)
## Summary - Instead of seven (more or less similar) `setup_db` functions, use just one in a single central place. - For every test that needs customization beyond that, offer a `TestDbBuilder` that can control the Python target version, custom typeshed, and pre-existing files. The main motivation for this is that we're soon going to need customization of the Python version, and I didn't feel like adding this to each of the existing `setup_db` functions.
1 parent 155d34b commit bd27bfa

File tree

9 files changed

+117
-201
lines changed

9 files changed

+117
-201
lines changed

crates/red_knot_python_semantic/src/db.rs

+68-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ pub trait Db: SourceDb + Upcast<dyn SourceDb> {
1111
pub(crate) mod tests {
1212
use std::sync::Arc;
1313

14+
use crate::program::{Program, SearchPathSettings};
15+
use crate::python_version::PythonVersion;
16+
use crate::ProgramSettings;
17+
18+
use anyhow::Context;
1419
use ruff_db::files::{File, Files};
15-
use ruff_db::system::{DbWithTestSystem, System, TestSystem};
20+
use ruff_db::system::{DbWithTestSystem, System, SystemPathBuf, TestSystem};
1621
use ruff_db::vendored::VendoredFileSystem;
1722
use ruff_db::{Db as SourceDb, Upcast};
1823

@@ -108,4 +113,66 @@ pub(crate) mod tests {
108113
events.push(event);
109114
}
110115
}
116+
117+
pub(crate) struct TestDbBuilder<'a> {
118+
/// Target Python version
119+
python_version: PythonVersion,
120+
/// Path to a custom typeshed directory
121+
custom_typeshed: Option<SystemPathBuf>,
122+
/// Path and content pairs for files that should be present
123+
files: Vec<(&'a str, &'a str)>,
124+
}
125+
126+
impl<'a> TestDbBuilder<'a> {
127+
pub(crate) fn new() -> Self {
128+
Self {
129+
python_version: PythonVersion::default(),
130+
custom_typeshed: None,
131+
files: vec![],
132+
}
133+
}
134+
135+
pub(crate) fn with_python_version(mut self, version: PythonVersion) -> Self {
136+
self.python_version = version;
137+
self
138+
}
139+
140+
pub(crate) fn with_custom_typeshed(mut self, path: &str) -> Self {
141+
self.custom_typeshed = Some(SystemPathBuf::from(path));
142+
self
143+
}
144+
145+
pub(crate) fn with_file(mut self, path: &'a str, content: &'a str) -> Self {
146+
self.files.push((path, content));
147+
self
148+
}
149+
150+
pub(crate) fn build(self) -> anyhow::Result<TestDb> {
151+
let mut db = TestDb::new();
152+
153+
let src_root = SystemPathBuf::from("/src");
154+
db.memory_file_system().create_directory_all(&src_root)?;
155+
156+
db.write_files(self.files)
157+
.context("Failed to write test files")?;
158+
159+
let mut search_paths = SearchPathSettings::new(src_root);
160+
search_paths.custom_typeshed = self.custom_typeshed;
161+
162+
Program::from_settings(
163+
&db,
164+
&ProgramSettings {
165+
target_version: self.python_version,
166+
search_paths,
167+
},
168+
)
169+
.context("Failed to configure Program settings")?;
170+
171+
Ok(db)
172+
}
173+
}
174+
175+
pub(crate) fn setup_db() -> TestDb {
176+
TestDbBuilder::new().build().expect("valid TestDb setup")
177+
}
111178
}

crates/red_knot_python_semantic/src/semantic_model.rs

+13-27
Original file line numberDiff line numberDiff line change
@@ -166,31 +166,15 @@ impl_binding_has_ty!(ast::ParameterWithDefault);
166166
mod tests {
167167
use ruff_db::files::system_path_to_file;
168168
use ruff_db::parsed::parsed_module;
169-
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
170-
171-
use crate::db::tests::TestDb;
172-
use crate::program::{Program, SearchPathSettings};
173-
use crate::python_version::PythonVersion;
174-
use crate::{HasTy, ProgramSettings, SemanticModel};
175-
176-
fn setup_db<'a>(files: impl IntoIterator<Item = (&'a str, &'a str)>) -> anyhow::Result<TestDb> {
177-
let mut db = TestDb::new();
178-
db.write_files(files)?;
179-
180-
Program::from_settings(
181-
&db,
182-
&ProgramSettings {
183-
target_version: PythonVersion::default(),
184-
search_paths: SearchPathSettings::new(SystemPathBuf::from("/src")),
185-
},
186-
)?;
187-
188-
Ok(db)
189-
}
169+
170+
use crate::db::tests::TestDbBuilder;
171+
use crate::{HasTy, SemanticModel};
190172

191173
#[test]
192174
fn function_ty() -> anyhow::Result<()> {
193-
let db = setup_db([("/src/foo.py", "def test(): pass")])?;
175+
let db = TestDbBuilder::new()
176+
.with_file("/src/foo.py", "def test(): pass")
177+
.build()?;
194178

195179
let foo = system_path_to_file(&db, "/src/foo.py").unwrap();
196180

@@ -207,7 +191,9 @@ mod tests {
207191

208192
#[test]
209193
fn class_ty() -> anyhow::Result<()> {
210-
let db = setup_db([("/src/foo.py", "class Test: pass")])?;
194+
let db = TestDbBuilder::new()
195+
.with_file("/src/foo.py", "class Test: pass")
196+
.build()?;
211197

212198
let foo = system_path_to_file(&db, "/src/foo.py").unwrap();
213199

@@ -224,10 +210,10 @@ mod tests {
224210

225211
#[test]
226212
fn alias_ty() -> anyhow::Result<()> {
227-
let db = setup_db([
228-
("/src/foo.py", "class Test: pass"),
229-
("/src/bar.py", "from foo import Test"),
230-
])?;
213+
let db = TestDbBuilder::new()
214+
.with_file("/src/foo.py", "class Test: pass")
215+
.with_file("/src/bar.py", "from foo import Test")
216+
.build()?;
231217

232218
let bar = system_path_to_file(&db, "/src/bar.py").unwrap();
233219

crates/red_knot_python_semantic/src/symbol.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl<'db> Symbol<'db> {
9090
#[cfg(test)]
9191
mod tests {
9292
use super::*;
93-
use crate::types::tests::setup_db;
93+
use crate::db::tests::setup_db;
9494

9595
#[test]
9696
fn test_symbol_or_fall_back_to() {

crates/red_knot_python_semantic/src/types.rs

+7-26
Original file line numberDiff line numberDiff line change
@@ -3203,38 +3203,16 @@ static_assertions::assert_eq_size!(Type, [u8; 16]);
32033203
#[cfg(test)]
32043204
pub(crate) mod tests {
32053205
use super::*;
3206-
use crate::db::tests::TestDb;
3207-
use crate::program::{Program, SearchPathSettings};
3208-
use crate::python_version::PythonVersion;
3206+
use crate::db::tests::{setup_db, TestDb, TestDbBuilder};
32093207
use crate::stdlib::typing_symbol;
3210-
use crate::ProgramSettings;
3208+
use crate::PythonVersion;
32113209
use ruff_db::files::system_path_to_file;
32123210
use ruff_db::parsed::parsed_module;
3213-
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
3211+
use ruff_db::system::DbWithTestSystem;
32143212
use ruff_db::testing::assert_function_query_was_not_run;
32153213
use ruff_python_ast as ast;
32163214
use test_case::test_case;
32173215

3218-
pub(crate) fn setup_db() -> TestDb {
3219-
let db = TestDb::new();
3220-
3221-
let src_root = SystemPathBuf::from("/src");
3222-
db.memory_file_system()
3223-
.create_directory_all(&src_root)
3224-
.unwrap();
3225-
3226-
Program::from_settings(
3227-
&db,
3228-
&ProgramSettings {
3229-
target_version: PythonVersion::default(),
3230-
search_paths: SearchPathSettings::new(src_root),
3231-
},
3232-
)
3233-
.expect("Valid search path settings");
3234-
3235-
db
3236-
}
3237-
32383216
/// A test representation of a type that can be transformed unambiguously into a real Type,
32393217
/// given a db.
32403218
#[derive(Debug, Clone, PartialEq)]
@@ -3839,7 +3817,10 @@ pub(crate) mod tests {
38393817

38403818
#[test]
38413819
fn typing_vs_typeshed_no_default() {
3842-
let db = setup_db();
3820+
let db = TestDbBuilder::new()
3821+
.with_python_version(PythonVersion::PY313)
3822+
.build()
3823+
.unwrap();
38433824

38443825
let typing_no_default = typing_symbol(&db, "NoDefault").expect_type();
38453826
let typing_extensions_no_default = typing_extensions_symbol(&db, "NoDefault").expect_type();

crates/red_knot_python_semantic/src/types/builder.rs

+4-25
Original file line numberDiff line numberDiff line change
@@ -378,35 +378,14 @@ impl<'db> InnerIntersectionBuilder<'db> {
378378
#[cfg(test)]
379379
mod tests {
380380
use super::{IntersectionBuilder, IntersectionType, Type, UnionType};
381-
use crate::db::tests::TestDb;
382-
use crate::program::{Program, SearchPathSettings};
383-
use crate::python_version::PythonVersion;
381+
382+
use crate::db::tests::{setup_db, TestDb};
384383
use crate::types::{global_symbol, todo_type, KnownClass, UnionBuilder};
385-
use crate::ProgramSettings;
384+
386385
use ruff_db::files::system_path_to_file;
387-
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
386+
use ruff_db::system::DbWithTestSystem;
388387
use test_case::test_case;
389388

390-
fn setup_db() -> TestDb {
391-
let db = TestDb::new();
392-
393-
let src_root = SystemPathBuf::from("/src");
394-
db.memory_file_system()
395-
.create_directory_all(&src_root)
396-
.unwrap();
397-
398-
Program::from_settings(
399-
&db,
400-
&ProgramSettings {
401-
target_version: PythonVersion::default(),
402-
search_paths: SearchPathSettings::new(src_root),
403-
},
404-
)
405-
.expect("Valid search path settings");
406-
407-
db
408-
}
409-
410389
#[test]
411390
fn build_union() {
412391
let db = setup_db();

crates/red_knot_python_semantic/src/types/display.rs

+2-23
Original file line numberDiff line numberDiff line change
@@ -357,31 +357,10 @@ impl Display for DisplayStringLiteralType<'_> {
357357
#[cfg(test)]
358358
mod tests {
359359
use ruff_db::files::system_path_to_file;
360-
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
360+
use ruff_db::system::DbWithTestSystem;
361361

362-
use crate::db::tests::TestDb;
362+
use crate::db::tests::setup_db;
363363
use crate::types::{global_symbol, SliceLiteralType, StringLiteralType, Type, UnionType};
364-
use crate::{Program, ProgramSettings, PythonVersion, SearchPathSettings};
365-
366-
fn setup_db() -> TestDb {
367-
let db = TestDb::new();
368-
369-
let src_root = SystemPathBuf::from("/src");
370-
db.memory_file_system()
371-
.create_directory_all(&src_root)
372-
.unwrap();
373-
374-
Program::from_settings(
375-
&db,
376-
&ProgramSettings {
377-
target_version: PythonVersion::default(),
378-
search_paths: SearchPathSettings::new(src_root),
379-
},
380-
)
381-
.expect("Valid search path settings");
382-
383-
db
384-
}
385364

386365
#[test]
387366
fn test_condense_literal_display_by_type() -> anyhow::Result<()> {

0 commit comments

Comments
 (0)