Skip to content

Commit

Permalink
[red-knot] Add more tests asserting that the VendoredFileSystem and t…
Browse files Browse the repository at this point in the history
…he `VERSIONS` parser work with the vendored typeshed stubs (#11970)
  • Loading branch information
AlexWaygood authored Jun 21, 2024
1 parent da79bac commit 3d0230f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/red_knot_module_resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ zip = { workspace = true }
[dev-dependencies]
anyhow = { workspace = true }
insta = { workspace = true }
path-slash = { workspace = true }
tempfile = { workspace = true }
walkdir = { workspace = true }

[lints]
workspace = true
74 changes: 65 additions & 9 deletions crates/red_knot_module_resolver/src/typeshed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ mod tests {
use std::io::{self, Read};
use std::path::Path;

#[test]
fn typeshed_zip_created_at_build_time() -> anyhow::Result<()> {
// The file path here is hardcoded in this crate's `build.rs` script.
// Luckily this crate will fail to build if this file isn't available at build time.
const TYPESHED_ZIP_BYTES: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/zipped_typeshed.zip"));
use path_slash::PathExt;

use ruff_db::vendored::VendoredFileSystem;
use ruff_db::vfs::VendoredPath;

let mut typeshed_zip_archive = zip::ZipArchive::new(io::Cursor::new(TYPESHED_ZIP_BYTES))?;
// The file path here is hardcoded in this crate's `build.rs` script.
// Luckily this crate will fail to build if this file isn't available at build time.
const TYPESHED_ZIP_BYTES: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/zipped_typeshed.zip"));

#[test]
fn typeshed_zip_created_at_build_time() {
let mut typeshed_zip_archive =
zip::ZipArchive::new(io::Cursor::new(TYPESHED_ZIP_BYTES)).unwrap();

let path_to_functools = Path::new("stdlib").join("functools.pyi");
let mut functools_module_stub = typeshed_zip_archive
Expand All @@ -21,9 +27,59 @@ mod tests {
assert!(functools_module_stub.is_file());

let mut functools_module_stub_source = String::new();
functools_module_stub.read_to_string(&mut functools_module_stub_source)?;
functools_module_stub
.read_to_string(&mut functools_module_stub_source)
.unwrap();

assert!(functools_module_stub_source.contains("def update_wrapper("));
Ok(())
}

#[test]
fn typeshed_vfs_consistent_with_vendored_stubs() {
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
let vendored_typeshed_stubs = VendoredFileSystem::new(TYPESHED_ZIP_BYTES).unwrap();

let mut empty_iterator = true;
for entry in walkdir::WalkDir::new(&vendored_typeshed_dir).min_depth(1) {
empty_iterator = false;
let entry = entry.unwrap();
let absolute_path = entry.path();
let file_type = entry.file_type();

let relative_path = absolute_path
.strip_prefix(&vendored_typeshed_dir)
.unwrap_or_else(|_| {
panic!("Expected {absolute_path:?} to be a child of {vendored_typeshed_dir:?}")
});

let posix_style_path = relative_path
.to_slash()
.unwrap_or_else(|| panic!("Expected {relative_path:?} to be a valid UTF-8 path"));

let vendored_path = VendoredPath::new(&*posix_style_path);

assert!(
vendored_typeshed_stubs.exists(vendored_path),
"Expected {vendored_path:?} to exist in the `VendoredFileSystem`!"
);

let vendored_path_kind = vendored_typeshed_stubs
.metadata(vendored_path)
.unwrap_or_else(|| {
panic!("Expected metadata for {vendored_path:?} to be retrievable from the `VendoredFileSystem!")
})
.kind();

assert_eq!(
vendored_path_kind.is_directory(),
file_type.is_dir(),
"{vendored_path:?} had type {vendored_path_kind:?}, inconsistent with fs path {relative_path:?}: {file_type:?}"
);
}

assert!(
!empty_iterator,
"Expected there to be at least one file or directory in the vendored typeshed stubs!"
);
}
}
57 changes: 57 additions & 0 deletions crates/red_knot_module_resolver/src/typeshed/versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,14 @@ impl From<SupportedPyVersion> for PyVersion {
#[cfg(test)]
mod tests {
use std::num::{IntErrorKind, NonZeroU16};
use std::path::Path;

use super::*;

use insta::assert_snapshot;

const TYPESHED_STDLIB_DIR: &str = "stdlib";

#[allow(unsafe_code)]
const ONE: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(1) };

Expand Down Expand Up @@ -345,6 +348,60 @@ mod tests {
assert!(!versions.module_exists_on_version(audioop, SupportedPyVersion::Py313));
}

#[test]
fn typeshed_versions_consistent_with_vendored_stubs() {
const VERSIONS_DATA: &str = include_str!("../../vendor/typeshed/stdlib/VERSIONS");
let vendored_typeshed_dir = Path::new("vendor/typeshed").canonicalize().unwrap();
let vendored_typeshed_versions = TypeshedVersions::from_str(VERSIONS_DATA).unwrap();

let mut empty_iterator = true;

let stdlib_stubs_path = vendored_typeshed_dir.join(TYPESHED_STDLIB_DIR);

for entry in std::fs::read_dir(&stdlib_stubs_path).unwrap() {
empty_iterator = false;
let entry = entry.unwrap();
let absolute_path = entry.path();

let relative_path = absolute_path
.strip_prefix(&stdlib_stubs_path)
.unwrap_or_else(|_| panic!("Expected path to be a child of {stdlib_stubs_path:?} but found {absolute_path:?}"));

let relative_path_str = relative_path.as_os_str().to_str().unwrap_or_else(|| {
panic!("Expected all typeshed paths to be valid UTF-8; got {relative_path:?}")
});
if relative_path_str == "VERSIONS" {
continue;
}

let top_level_module = if let Some(extension) = relative_path.extension() {
// It was a file; strip off the file extension to get the module name:
let extension = extension
.to_str()
.unwrap_or_else(||panic!("Expected all file extensions to be UTF-8; was not true for {relative_path:?}"));

relative_path_str
.strip_suffix(extension)
.and_then(|string| string.strip_suffix('.')).unwrap_or_else(|| {
panic!("Expected path {relative_path_str:?} to end with computed extension {extension:?}")
})
} else {
// It was a directory; no need to do anything to get the module name
relative_path_str
};

let top_level_module = ModuleName::new(top_level_module)
.unwrap_or_else(|| panic!("{top_level_module:?} was not a valid module name!"));

assert!(vendored_typeshed_versions.contains_module(&top_level_module));
}

assert!(
!empty_iterator,
"Expected there to be at least one file or directory in the vendored typeshed stubs"
);
}

#[test]
fn can_parse_mock_versions_file() {
const VERSIONS: &str = "\
Expand Down

0 comments on commit 3d0230f

Please sign in to comment.