New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(buffer): deprecate assert_buffer_eq! in favor of assert_eq! #1007
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #1007 +/- ##
======================================
Coverage 94.2% 94.3%
======================================
Files 61 61
Lines 14587 14768 +181
======================================
+ Hits 13751 13932 +181
Misses 836 836 ☔ View full report in Codecov by Sentry. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
- I'd suggest fixing the "needless pass by value" lints rather than allowing them, but this is a small thing in comparison to the larger change.
I wonder how possible it would be to accept Into for the expected and then make it possible to just pass a bare Iterator / IntoIterator of Into values.
E.g.
buf.assert_eq(&Buffer::with_lines(vec![" foo", " bar "]));
Becomes:
buf.assert_eq([" foo", " bar "]);
// or perhaps it's meaningful or necessary to disambiguate:
buf.assert_lines([...]);
I am not sure if I like that idea. The idea of |
This comment was marked as outdated.
This comment was marked as outdated.
My rationale on this is that we have a huge amount of tests that call that A well written test IMHO is one that can be grokked as correct just by looking at it. Such tests are an exercise in removing all details that are unnecessary to prove the desired assertion. Smaller tests are often better, so long as there's no (unreasonable) complexity added by DRYing up the tests. If there's a large scale repetition like this, then it's really worth considering how things look when the repetition it's removed. I'd like to get some consensus from the other @ratatui-org/maintainers on whether to DRY the tests up or not before merging this, and make this as a single change rather than adding this new method and then removing or changing it later. Perhaps an alternative test approach to consider could be something like: assert_eq!(actual.lines(), [
"hello world!".into(),
"goodbye worl".into(),
]); P.s. I didn't realize the lint I asked you to fix would be affect such a large amount of code - my apologies. |
I like that we are discussing alternatives to improve the usage of this! Writing impl Buffer {
/// Asserts that this `Buffer` equals the expected lines
pub fn assert_lines<'l, I>(&self, expected: I)
where
I: IntoIterator,
I::Item: Into<Line<'l>>,
{
self.assert_eq(&Self::with_lines(expected));
}
} The usage is very similar: -one.assert_eq(&Buffer::with_lines(["11", "11", "22", "22"]));
+one.assert_lines(["11", "11", "22", "22"]); When the expected Buffer has more complicated styles this currently is written as
Also side note: Into can sneak in stuff like clone() which is not obvious from looking at the code. For example EdJoPaTo/tui-rs-tree-widget@b4d0c30 took a reference and into cloned it while the caller dropped the value later resulting in a superfluous clone on every render which was invisible from looking at the code. Therefore, I like to be more explicit instead of using into in order to not introduce hidden performance relevant mistakes. Your -one.assert_eq(&Buffer::with_lines(["11", "11", "22", "22"]));
+assert_eq!(one.lines(), [
+ "11".into(),
+ "11".into(),
+ "22".into(),
+ "22".into(),
+]); And this still does not highlight the exact diffs like the current implementation does with I think the only real alternative is the usage of ---- buffer::assert::tests::assert_buffer_eq_panics_on_unequal_area stdout ----
thread 'buffer::assert::tests::assert_buffer_eq_panics_on_unequal_area' panicked at src/buffer/assert.rs:70:9:
-buffer areas not equal
-expected: Buffer {
+assertion `left == right` failed
+ left: Buffer {
area: Rect { x: 0, y: 0, width: 6, height: 1 },
content: [
" ",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
]
}
-actual: Buffer {
+ right: Buffer {
area: Rect { x: 0, y: 0, width: 5, height: 1 },
content: [
" ",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
]
}
---- buffer::assert::tests::assert_buffer_eq_panics_on_unequal_style stdout ----
thread 'buffer::assert::tests::assert_buffer_eq_panics_on_unequal_style' panicked at src/buffer/assert.rs:78:9:
-buffer contents not equal
-expected: Buffer {
+assertion `left == right` failed
+ left: Buffer {
area: Rect { x: 0, y: 0, width: 5, height: 1 },
content: [
" ",
],
styles: [
x: 0, y: 0, fg: Red, bg: Reset, underline: Reset, modifier: NONE,
x: 1, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
]
}
-actual: Buffer {
+ right: Buffer {
area: Rect { x: 0, y: 0, width: 5, height: 1 },
content: [
" ",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE,
]
}
-diff:
-0: at (0, 0)
- expected: Cell { symbol: " ", fg: Red, bg: Reset, underline_color: Reset, modifier: NONE, skip: false }
- actual: Cell { symbol: " ", fg: Reset, bg: Reset, underline_color: Reset, modifier: NONE, skip: false } This is fine for smaller Buffer comparisons but gets annoying for bigger diffs. For example the Map widget tests have hundreds of Cells. Finding a single difference in there is annoying. So… I would stick with the |
This comment was marked as outdated.
This comment was marked as outdated.
ca6fde2
to
d21f850
Compare
It feels odd to me that test code is hanging off the Buffer. I think this might feel more appropriate as a standalone function to be imported rather than a method in the Buffer implementation. I haven't ever seen other places where the test code ends up as a method, and a function to be imported seems like a reasonable compromise. Thoughts? To be clear, I'm suggesting: fn assert_eq(left: Buffer, right: Buffer) {} or assert_buffer_eq. Either seems fine, assert_eq is shorter, which makes tests easier to read, but could be mistaken for assert_eq! easily. Another approach to keeping the assert_eq out of the public could be trait AssertEq {
fn assert_eq(&self, other: &Self);
}
impl AssertEq for Buffer { ...} I really like that this removes 600 LoC. That's a bunch less to think about. I'd really like to simplify the assertions more - perhaps By keeping this out of the buffer and as a separate function, it makes it obvious where other similar functions like that would end up (rather than bloating |
I think having this existing on the It could be explicitly not on self, but I'm not sure why as its kinda clear to me what is happening in that case as it is used on a buffer.
Like prelude is bloating the users namespace which results in uncertainty where something is coming from and what is used? 🔥
I don't think this should be kept out of public code as people either don't use Buffer directly (just passing it around where its needed) or use it when creating widgets, and it's really helpful for testing your code works so it should be discoverable. Maybe add to the doc comment that this is helpful for test cases and add an example for that in there?
|
I think this idea works great. It's super clear what it's doing, while still being similar enough to the existing
This only bloats namespaces for users that opt in to using the prelude. I'd assume your apps / widgets would be marked Perhaps a followup PR for that change is in order - let's get this over the line and I'll slap one together. |
Just to make sure I understand you correctly there. I preferred not to add this as this isn’t much shorter and less clear what is happening. Your follow up PR still wants to add it? |
I think this is an argument against using a more generic trait and going with a simple implementation. This shouldn't lead people to creating more things like this. This is a workaround around the Going with |
funny realisation: |
Thought about adding |
This also works with the native So maybe the best approach is to remove the dedicated buffer assert stuff all together? Adding a #[rustfmt::skip]
let expected = Buffer::with_lines([
" a",
" b",
" c",
]);
let expected = Buffer::with_lines([" a", " b", " c"]); This way the buffer content can get way smaller resulting in smaller Debug outputs and therefore easier to spot differences for test cases. Then the need for a special macro for this is even less important. If that still requires a diff as it's still complicated to spot the difference then a (temporary?) That would remove the somewhat weird custom |
To make that work, the debug format also needs to be much less verbose (but in a way that makes it easy to identify differences). For the most part, the visual inspection approach applies to multi-line output in the buffers. I'm not opposed to
Makes sense I guess. Perhaps give it a try and see how it looks with just |
e22c1b6
to
f5c88d7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work in wrangling all the tests into a really consistent form and making them into rstests when appropriate. I went through the entire PR with a pretty detailed view, and only found the comments previously raised to get answers to / fixes.
I think this is ready to merge, but I'm 50/50 on whether to wait til after 0.26.3 to merge (so we can get out the unicode line bug fix).
Pros: (less carrying cost of large PR, less future PR conflicts)
Cons: (potential breakage so close to releasing a patch release)
Your call on whether to merge this.
Future PR conflicts is not really an argument as stuff isn't branched of release tags, they are branched of from the default branch.
This changes mostly tests. Only the macro itself is a non-test change. And it fixes #1043. I could see why removing the deprecation might be a good idea until after the patch release, but it's really just a deprecation.
Therefore, I would go for merging this (once the open points are all resolved). |
Any improvements for the first message (above the line) as a commit message? When not, this should be merged. |
LGTM |
…atatui-org#1007) - Simplify `assert_buffer_eq!` logic. - Deprecate `assert_buffer_eq!`. - Introduce `TestBackend::assert_buffer_lines`. Also simplify many tests involving buffer comparisons. For the deprecation, just use `assert_eq` instead of `assert_buffer_eq`: ```diff -assert_buffer_eq!(actual, expected); +assert_eq!(actual, expected); ``` --- I noticed `assert_buffer_eq!` creating no test coverage reports and looked into this macro. First I simplified it. Then I noticed a bunch of `assert_eq!(buffer, …)` and other indirect usages of this macro (like `TestBackend::assert_buffer`). The good thing here is that it's mainly used in tests so not many changes to the library code.
assert_buffer_eq!
logic.assert_buffer_eq!
.TestBackend::assert_buffer_lines
.Also simplify many tests involving buffer comparisons.
For the deprecation, just use
assert_eq
instead ofassert_buffer_eq
:I noticed
assert_buffer_eq!
creating no test coverage reports and looked into this macro. First I simplified it. Then I noticed a bunch ofassert_eq!(buffer, …)
and other indirect usages of this macro (likeTestBackend::assert_buffer
).The good thing here is that it's mainly used in tests so not many changes to the library code.