Skip to content

Commit 26fb058

Browse files
msullivanmeta-codesync[bot]
authored andcommitted
Add tests for find_sources, gen-source-db, run-tree (#7)
Summary: Most of the tests test the shared find_sources code directly, but also add a smoke test each for run-tree and gen-source-db that verify they are both supporting site-packages. Pull Request resolved: #7 Reviewed By: martindemello Differential Revision: D102024363 Pulled By: brittanyrey fbshipit-source-id: dfa05d6fd773a3ece6d7a76d6698dc08a099cced
1 parent d4ef26a commit 26fb058

7 files changed

Lines changed: 202 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ serde = { version = "1.0.219", features = ["derive", "rc"] }
3535
serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] }
3636
starlark_map = "0.13.0"
3737
static_interner = "0.1.1"
38+
tempfile = "3.27.0"
3839
toml = { version = "0.9.12", features = ["preserve_order"] }
3940
tracing = { version = "0.1.41", features = ["attributes", "valuable"] }
4041
tracing-subscriber = { version = "0.3.23", features = ["chrono", "env-filter", "json", "local-time", "parking_lot", "registry"] }

src/test_lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use std::collections::HashMap;
99
use std::collections::HashSet;
10+
use std::fs;
1011
use std::process::Command;
1112

1213
use ahash::AHashMap;
@@ -15,6 +16,7 @@ use itertools::Itertools;
1516
use pyrefly_python::module::Module;
1617
use pyrefly_python::module_name::ModuleName;
1718
use rayon::prelude::*;
19+
use tempfile::TempDir;
1820

1921
use crate::analyzer::analyze;
2022
use crate::effects::Effect;
@@ -445,3 +447,18 @@ pub fn check_buck_availability() -> bool {
445447
}
446448
}
447449
}
450+
451+
/// Create a new temp directory and write each `(rel_path, contents)` pair
452+
/// into it, creating intermediate directories as needed. The returned
453+
/// [`TempDir`] owns the path and deletes it on drop.
454+
pub fn populate_temp_dir(files: &[(&str, &str)]) -> TempDir {
455+
let tmp = TempDir::new().expect("create temp dir");
456+
for (rel, contents) in files {
457+
let path = tmp.path().join(rel);
458+
if let Some(parent) = path.parent() {
459+
fs::create_dir_all(parent).expect("create parent dirs");
460+
}
461+
fs::write(&path, contents).expect("write file");
462+
}
463+
tmp
464+
}

tests/Cargo.toml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# @generated by autocargo from //safer_lazy_imports/lifeguard/tests:[assign,attrs,basic,blocks,calls,constructors,decorators,effects,encoding,exceptions,imports,nested_functions,nested_imports,output,param_mutation,port_test_analyzer,port_test_catch_implicit_imports,port_test_catch_import_cycles,port_test_finalization,port_test_interpreter,port_test_ownership,source_analyzer,star_imports,stubs]
1+
# @generated by autocargo from //safer_lazy_imports/lifeguard/tests:[assign,attrs,basic,blocks,calls,constructors,decorators,effects,encoding,exceptions,find_sources,gen_source_db,imports,nested_functions,nested_imports,output,param_mutation,port_test_analyzer,port_test_catch_implicit_imports,port_test_catch_import_cycles,port_test_finalization,port_test_interpreter,port_test_ownership,run_tree,source_analyzer,star_imports,stubs]
22

33
[package]
44
name = "safer_lazy_imports_lifeguard_tests"
@@ -48,6 +48,14 @@ path = "encoding.rs"
4848
name = "exceptions"
4949
path = "exceptions.rs"
5050

51+
[[test]]
52+
name = "find_sources"
53+
path = "find_sources.rs"
54+
55+
[[test]]
56+
name = "gen_source_db"
57+
path = "gen_source_db.rs"
58+
5159
[[test]]
5260
name = "imports"
5361
path = "imports.rs"
@@ -92,6 +100,10 @@ path = "port_test_interpreter.rs"
92100
name = "port_test_ownership"
93101
path = "port_test_ownership.rs"
94102

103+
[[test]]
104+
name = "run_tree"
105+
path = "run_tree.rs"
106+
95107
[[test]]
96108
name = "source_analyzer"
97109
path = "source_analyzer.rs"
@@ -106,6 +118,7 @@ path = "stubs.rs"
106118

107119
[dev-dependencies]
108120
anyhow = "1.0.102"
121+
clap = { version = "4.6.0", features = ["derive", "env", "string", "unicode", "wrap_help"] }
109122
lifeguard = { path = "../src" }
110123
ruff_text_size = { git = "https://github.com/astral-sh/ruff/", rev = "474b00568ad78f02ad8e19b8166cbeb6d69f8511" }
111124
serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "raw_value", "unbounded_depth"] }

tests/find_sources.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use std::collections::BTreeMap;
11+
use std::collections::BTreeSet;
12+
13+
use lifeguard::find_sources::build_source_db;
14+
use lifeguard::test_lib::populate_temp_dir;
15+
16+
fn keys(build_map: &BTreeMap<String, String>) -> BTreeSet<&str> {
17+
build_map.keys().map(|s| s.as_str()).collect()
18+
}
19+
20+
#[test]
21+
fn test_seeds_py_files_from_input_dir() {
22+
let tmp = populate_temp_dir(&[("a.py", ""), ("pkg/__init__.py", ""), ("pkg/b.py", "")]);
23+
24+
let (build_map, seed_count) = build_source_db(tmp.path(), None).unwrap();
25+
assert_eq!(seed_count, 3, "all three .py files are seeded");
26+
assert_eq!(
27+
keys(&build_map),
28+
BTreeSet::from(["a.py", "pkg/__init__.py", "pkg/b.py"]),
29+
);
30+
}
31+
32+
#[test]
33+
fn test_skips_non_identifier_names() {
34+
let tmp = populate_temp_dir(&[
35+
("good.py", ""),
36+
// Dir whose name is not a valid identifier: skipped wholesale.
37+
(".venv/bad.py", ""),
38+
// File whose stem is not a valid identifier: skipped.
39+
("2024-migration.py", ""),
40+
]);
41+
42+
let (build_map, _) = build_source_db(tmp.path(), None).unwrap();
43+
assert_eq!(keys(&build_map), BTreeSet::from(["good.py"]));
44+
}
45+
46+
#[test]
47+
fn test_follows_imports_into_site_packages() {
48+
let tmp = populate_temp_dir(&[
49+
("proj/main.py", "import foo\n"),
50+
("sp/foo/__init__.py", ""),
51+
// Unreachable from main.py's imports — must not be pulled in.
52+
("sp/foo/helper.py", ""),
53+
("sp/unused/__init__.py", ""),
54+
]);
55+
let proj = tmp.path().join("proj");
56+
let sp = tmp.path().join("sp");
57+
58+
let (build_map, seed_count) = build_source_db(&proj, Some(&sp)).unwrap();
59+
assert_eq!(seed_count, 1, "only main.py is seeded from the project");
60+
assert_eq!(
61+
keys(&build_map),
62+
BTreeSet::from(["main.py", "foo/__init__.py"]),
63+
);
64+
}
65+
}

tests/gen_source_db.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use std::collections::BTreeSet;
11+
use std::fs;
12+
13+
use clap::Parser;
14+
use lifeguard::commands::gen_source_db::GenSourceDbArgs;
15+
use lifeguard::commands::gen_source_db::run;
16+
use lifeguard::test_lib::populate_temp_dir;
17+
use serde_json::Value;
18+
19+
#[test]
20+
fn test_gen_source_db_writes_build_map_json() {
21+
let tmp = populate_temp_dir(&[
22+
// Site-packages location is discovered via pyproject.toml rather
23+
// than passed on the CLI.
24+
(
25+
"proj/pyproject.toml",
26+
"[lifeguard]\nsite_packages = \"../sp\"\n",
27+
),
28+
("proj/m.py", "import other\n"),
29+
("sp/other/__init__.py", ""),
30+
]);
31+
let proj = tmp.path().join("proj");
32+
let output = tmp.path().join("db.json");
33+
34+
let args = GenSourceDbArgs::try_parse_from([
35+
"gen-source-db",
36+
proj.to_str().unwrap(),
37+
output.to_str().unwrap(),
38+
])
39+
.unwrap();
40+
run(args).unwrap();
41+
42+
let content = fs::read_to_string(&output).unwrap();
43+
let value: Value = serde_json::from_str(&content).unwrap();
44+
let build_map = value["build_map"]
45+
.as_object()
46+
.expect("build_map object in output JSON");
47+
let keys: BTreeSet<&str> = build_map.keys().map(|s| s.as_str()).collect();
48+
assert_eq!(keys, BTreeSet::from(["m.py", "other/__init__.py"]));
49+
}
50+
}

tests/run_tree.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#[cfg(test)]
9+
mod tests {
10+
use std::collections::BTreeSet;
11+
use std::fs;
12+
13+
use clap::Parser;
14+
use lifeguard::commands::run_tree::RunTreeArgs;
15+
use lifeguard::commands::run_tree::run;
16+
use lifeguard::test_lib::populate_temp_dir;
17+
use serde_json::Value;
18+
19+
#[test]
20+
fn test_run_tree_resolves_cli_site_packages() {
21+
let tmp = populate_temp_dir(&[
22+
("proj/main.py", "import foo\n"),
23+
("sp/foo/__init__.py", ""),
24+
("sp/bar/__init__.py", ""),
25+
]);
26+
let proj = tmp.path().join("proj");
27+
let sp = tmp.path().join("sp");
28+
let output = tmp.path().join("out.json");
29+
30+
let args = RunTreeArgs::try_parse_from([
31+
"run-tree",
32+
proj.to_str().unwrap(),
33+
output.to_str().unwrap(),
34+
"--site-packages",
35+
sp.to_str().unwrap(),
36+
"--sorted-output",
37+
])
38+
.unwrap();
39+
run(args).unwrap();
40+
41+
let content = fs::read_to_string(&output).unwrap();
42+
let value: Value = serde_json::from_str(&content).unwrap();
43+
let modules: BTreeSet<&str> = value["LAZY_ELIGIBLE"]
44+
.as_object()
45+
.expect("LAZY_ELIGIBLE object in output JSON")
46+
.keys()
47+
.map(|s| s.as_str())
48+
.collect();
49+
// `foo` appears only because --site-packages caused its resolution.
50+
// `bar` doesn't.
51+
assert_eq!(modules, BTreeSet::from(["main", "foo"]));
52+
}
53+
}

0 commit comments

Comments
 (0)