update tests

This commit is contained in:
Douglas Creager 2025-10-09 08:08:57 -04:00
parent 8426cc6915
commit 7b88440fc2
3 changed files with 190 additions and 32 deletions

21
PLAN.md
View File

@ -71,11 +71,10 @@ Add support for hover assertions in the mdtest framework. These assertions will
- [x] Handle `@Todo` metadata stripping in hover assertions
### 6. Add tests
**Status:** In progress
**Status:** ✅ Completed
- [x] Create simple mdtest file with working hover assertion examples (hover_simple.md)
- [ ] Create comprehensive mdtest file with edge cases (hover.md - partially complete)
- [ ] Add unit tests for hover assertion parsing in ty_test
- [x] Create comprehensive mdtest file with edge cases (hover.md)
- [x] Add unit tests for hover assertion parsing in ty_test
## Key Design Decisions
@ -135,10 +134,9 @@ def foo() -> int: ...
- Implemented hover matching logic comparing inferred vs expected types
- **All core functionality now complete and compiling!**
- **2025-10-08**: Step 6 in progress - Added test files and refined implementation
- Created hover_simple.md mdtest with working examples
- Created comprehensive hover.md mdtest file with edge cases
- Fixed infer_type_at_position to handle expression statements (StmtExpr nodes)
- Learned that arrow positioning must align exactly with target expression characters
- hover.md created but needs arrow alignment fixes (arrows must point to exact character positions)
- **2025-10-08**: Refactored to use ty_ide's existing infrastructure
- User feedback: original suggestion to avoid ty_ide dependency was a mistake
- Made ty_test::Db implement ty_project::Db (added project field)
@ -148,3 +146,14 @@ def foo() -> int: ...
- Updated hover.rs to use ty_ide::find_goto_target instead of custom find_covering_node
- All tests pass with the refactored implementation
- **Implementation now uses ty_ide's existing covering node logic as requested**
- **2025-10-08**: Completed step 6 - Added comprehensive unit tests
- Removed hover_simple.md (no longer needed, hover.md is comprehensive)
- Added 6 unit tests for hover assertion parsing in ty_test/src/assertion.rs:
- hover_basic: Basic hover assertion parsing
- hover_with_spaces_before_arrow: Arrow with leading whitespace
- hover_complex_type: Complex type with @Todo metadata
- hover_multiple_on_same_line: Multiple hover assertions on same target line
- hover_mixed_with_other_assertions: Hover mixed with error assertions
- hover_parsed_column: Verify column extraction from arrow position
- All 104 ty_test unit tests pass
- **All implementation steps now complete!**

View File

@ -1,19 +0,0 @@
# Simple hover test
Testing basic hover functionality with simple cases.
```py
# Test 1: Simple variable with longer name
my_value = 10
#↓ hover: Literal[10]
my_value
# Test 2: Try hovering directly on the number literal
# ↓ hover: Literal[10]
some_var = 10
# Test 3: Variable reference with longer name
another_var = 42
#↓ hover: Literal[42]
another_var
```

View File

@ -317,7 +317,7 @@ impl std::fmt::Display for UnparsedAssertion<'_> {
}
/// An assertion comment that has been parsed and validated for correctness.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum ParsedAssertion<'a> {
/// A `# revealed:` assertion.
Revealed(&'a str),
@ -340,7 +340,7 @@ impl std::fmt::Display for ParsedAssertion<'_> {
}
/// A parsed and validated `# error:` assertion comment.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct ErrorAssertion<'a> {
/// The diagnostic rule code we expect.
pub(crate) rule: Option<&'a str>,
@ -375,7 +375,7 @@ impl std::fmt::Display for ErrorAssertion<'_> {
}
/// A parsed and validated `# hover:` assertion comment.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct HoverAssertion<'a> {
/// The one-based character column (UTF-32) in the line where the down arrow appears.
/// This indicates the character position in the target line where we should hover.
@ -403,7 +403,8 @@ impl<'a> HoverAssertion<'a> {
.ok_or(HoverAssertionParseError::MissingDownArrow)?;
// Calculate the TextSize position of the down arrow in the source file
let arrow_position = comment_range.start() + TextSize::try_from(arrow_byte_offset_in_comment).unwrap();
let arrow_position =
comment_range.start() + TextSize::try_from(arrow_byte_offset_in_comment).unwrap();
// Get the line and character column of the down arrow
let arrow_line_col = line_index.line_column(arrow_position, source);
@ -536,7 +537,7 @@ impl<'a> ErrorAssertionParser<'a> {
/// Enumeration of ways in which parsing an assertion comment can fail.
///
/// The assertion comment could be a "revealed", "error", or "hover" assertion.
#[derive(Debug, thiserror::Error)]
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub(crate) enum PragmaParseError<'a> {
#[error("Must specify which type should be revealed")]
EmptyRevealTypeAssertion,
@ -547,7 +548,7 @@ pub(crate) enum PragmaParseError<'a> {
}
/// Enumeration of ways in which parsing an *error* assertion comment can fail.
#[derive(Debug, thiserror::Error)]
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub(crate) enum ErrorAssertionParseError<'a> {
#[error("no rule or message text")]
NoRuleOrMessage,
@ -570,7 +571,7 @@ pub(crate) enum ErrorAssertionParseError<'a> {
}
/// Enumeration of ways in which parsing a *hover* assertion comment can fail.
#[derive(Debug, thiserror::Error)]
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub(crate) enum HoverAssertionParseError {
#[error("Hover assertion must contain a down arrow (↓) to indicate position")]
MissingDownArrow,
@ -907,4 +908,171 @@ mod tests {
r#"error: 1 [unbound-name] "`x` is unbound""#
);
}
#[test]
fn hover_basic() {
let assertions = get_assertions(&dedent(
"
# hover: int
x
",
));
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(2));
let [assert] = &line.assertions[..] else {
panic!("expected one assertion");
};
assert_eq!(format!("{assert}"), "hover: int");
}
#[test]
fn hover_with_spaces_before_arrow() {
let assertions = get_assertions(&dedent(
"
# hover: str
value
",
));
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(2));
let [assert] = &line.assertions[..] else {
panic!("expected one assertion");
};
assert_eq!(format!("{assert}"), "hover: str");
}
#[test]
fn hover_complex_type() {
let assertions = get_assertions(&dedent(
"
# hover: list[@Todo(list comprehension element type)]
result
",
));
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(2));
let [assert] = &line.assertions[..] else {
panic!("expected one assertion");
};
assert_eq!(
format!("{assert}"),
"hover: list[@Todo(list comprehension element type)]"
);
}
#[test]
fn hover_multiple_on_same_line() {
let assertions = get_assertions(&dedent(
"
# hover: Literal[1]
# hover: Literal[2]
x = 1 + 2
",
));
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(3));
let [assert1, assert2] = &line.assertions[..] else {
panic!("expected two assertions");
};
assert_eq!(format!("{assert1}"), "hover: Literal[1]");
assert_eq!(format!("{assert2}"), "hover: Literal[2]");
}
#[test]
fn hover_mixed_with_other_assertions() {
let assertions = get_assertions(&dedent(
"
# hover: int
# error: [some-error]
x
",
));
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(3));
let [assert1, assert2] = &line.assertions[..] else {
panic!("expected two assertions");
};
assert_eq!(format!("{assert1}"), "hover: int");
assert_eq!(format!("{assert2}"), "error: [some-error]");
}
#[test]
fn hover_parsed_column() {
use ruff_db::files::system_path_to_file;
let mut db = Db::setup();
let settings = ProgramSettings {
python_version: PythonVersionWithSource::default(),
python_platform: PythonPlatform::default(),
search_paths: SearchPathSettings::new(Vec::new())
.to_search_paths(db.system(), db.vendored())
.unwrap(),
};
Program::init_or_update(&mut db, settings);
let source_code = dedent(
"
# hover: Literal[10]
value = 10
",
);
db.write_file("/src/test.py", &source_code).unwrap();
let file = system_path_to_file(&db, "/src/test.py").unwrap();
let assertions = InlineFileAssertions::from_file(&db, file);
let [line] = &as_vec(&assertions)[..] else {
panic!("expected one line");
};
assert_eq!(line.line_number, OneIndexed::from_zero_indexed(2));
let [assert] = &line.assertions[..] else {
panic!("expected one assertion");
};
// Parse the assertion to verify column is extracted correctly
let source = ruff_db::source::source_text(&db, file);
let lines = ruff_db::source::line_index(&db, file);
let parsed = assert.parse(&lines, &source);
assert_eq!(
parsed,
Ok(ParsedAssertion::Hover(HoverAssertion {
column: OneIndexed::from_zero_indexed(7),
expected_type: "Literal[10]"
}))
);
}
}