Skip to content

Commit 599fe7c

Browse files
Merge pull request #253 from google:buffered-summary-refactor
PiperOrigin-RevId: 553777118
2 parents aa39332 + 18c67a2 commit 599fe7c

File tree

1 file changed

+125
-48
lines changed

1 file changed

+125
-48
lines changed

googletest/src/matcher_support/summarize_diff.rs

Lines changed: 125 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub(crate) fn create_diff(
4747
"\nDifference({} / {}):{}",
4848
LineStyle::extra_actual_style().style("actual"),
4949
LineStyle::extra_expected_style().style("expected"),
50-
edit_list_summary(&edit_list)
50+
edit_list.into_iter().collect::<BufferedSummary>(),
5151
)
5252
.into(),
5353
edit_distance::Difference::Unrelated => "".into(),
@@ -83,73 +83,150 @@ pub(crate) fn create_diff_reversed(
8383
"\nDifference({} / {}):{}",
8484
LineStyle::extra_actual_style().style("actual"),
8585
LineStyle::extra_expected_style().style("expected"),
86-
edit_list_summary(&edit_list)
86+
edit_list.into_iter().collect::<BufferedSummary>(),
8787
)
8888
.into()
8989
}
9090
edit_distance::Difference::Unrelated => "".into(),
9191
}
9292
}
9393

94-
fn edit_list_summary(edit_list: &[edit_distance::Edit<&str>]) -> String {
95-
let mut summary = String::new();
96-
// Use to collect common line and compress them.
97-
let mut common_line_buffer = vec![];
98-
for edit in edit_list {
99-
let (style, line) = match edit {
100-
edit_distance::Edit::Both(same) => {
101-
common_line_buffer.push(*same);
102-
continue;
103-
}
104-
edit_distance::Edit::ExtraActual(actual) => (LineStyle::extra_actual_style(), *actual),
105-
edit_distance::Edit::ExtraExpected(expected) => {
106-
(LineStyle::extra_expected_style(), *expected)
107-
}
108-
edit_distance::Edit::AdditionalActual => {
109-
(LineStyle::comment_style(), "<---- remaining lines omitted ---->")
110-
}
111-
};
112-
summary.push_str(&compress_common_lines(std::mem::take(&mut common_line_buffer)));
94+
// Aggregator collecting the lines to be printed in the difference summary.
95+
//
96+
// This is buffered in order to allow a future line to potentially impact how
97+
// the current line would be printed.
98+
struct BufferedSummary<'a> {
99+
summary: String,
100+
buffer: Buffer<'a>,
101+
}
102+
103+
impl<'a> BufferedSummary<'a> {
104+
// Appends a new line which is common to both actual and expected.
105+
fn feed_common_lines(&mut self, common_line: &'a str) {
106+
let Buffer::CommonLineBuffer(ref mut common_lines) = self.buffer;
107+
common_lines.push(common_line);
108+
}
109+
110+
// Appends a new line which is found only in the actual string.
111+
fn feed_extra_actual(&mut self, extra_actual: &'a str) {
112+
self.buffer.flush(&mut self.summary).unwrap();
113+
write!(&mut self.summary, "\n{}", LineStyle::extra_actual_style().style(extra_actual))
114+
.unwrap();
115+
}
113116

114-
write!(&mut summary, "\n{}", style.style(line)).unwrap();
117+
// Appends a new line which is found only in the expected string.
118+
fn feed_extra_expected(&mut self, extra_expected: &str) {
119+
self.flush_buffer();
120+
write!(&mut self.summary, "\n{}", LineStyle::extra_expected_style().style(extra_expected))
121+
.unwrap();
115122
}
116-
summary.push_str(&compress_common_lines(common_line_buffer));
117123

118-
summary
124+
// Appends a comment for the additional line at the start or the end of the
125+
// actual string which should be omitted.
126+
fn feed_additional_actual(&mut self) {
127+
self.flush_buffer();
128+
write!(
129+
&mut self.summary,
130+
"\n{}",
131+
LineStyle::comment_style().style("<---- remaining lines omitted ---->")
132+
)
133+
.unwrap();
134+
}
135+
136+
fn flush_buffer(&mut self) {
137+
self.buffer.flush(&mut self.summary).unwrap();
138+
}
119139
}
120140

121-
// The number of the lines kept before and after the compressed lines.
122-
const COMMON_LINES_CONTEXT_SIZE: usize = 2;
141+
impl<'a> FromIterator<edit_distance::Edit<&'a str>> for BufferedSummary<'a> {
142+
fn from_iter<T: IntoIterator<Item = edit_distance::Edit<&'a str>>>(iter: T) -> Self {
143+
let mut buffered_summary =
144+
BufferedSummary { summary: String::new(), buffer: Buffer::CommonLineBuffer(vec![]) };
145+
for edit in iter {
146+
match edit {
147+
edit_distance::Edit::Both(same) => {
148+
buffered_summary.feed_common_lines(same);
149+
}
150+
edit_distance::Edit::ExtraActual(actual) => {
151+
buffered_summary.feed_extra_actual(actual);
152+
}
153+
edit_distance::Edit::ExtraExpected(expected) => {
154+
buffered_summary.feed_extra_expected(expected);
155+
}
156+
edit_distance::Edit::AdditionalActual => {
157+
buffered_summary.feed_additional_actual();
158+
}
159+
};
160+
}
161+
buffered_summary.flush_buffer();
123162

124-
fn compress_common_lines(common_lines: Vec<&str>) -> String {
125-
if common_lines.len() <= 2 * COMMON_LINES_CONTEXT_SIZE + 1 {
126-
let mut all_lines = String::new();
127-
for line in common_lines {
128-
write!(&mut all_lines, "\n{}", LineStyle::unchanged_style().style(line)).unwrap();
163+
buffered_summary
164+
}
165+
}
166+
167+
impl<'a> Display for BufferedSummary<'a> {
168+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169+
if !matches!(self.buffer, Buffer::CommonLineBuffer(ref b) if b.is_empty()) {
170+
panic!("Buffer is not empty. This is a bug in gtest_rust.")
129171
}
130-
return all_lines;
172+
self.summary.fmt(f)
131173
}
174+
}
132175

133-
let mut truncated_lines = String::new();
176+
// This needs to be an enum as there will be in a follow-up PR new types of
177+
// buffer, most likely actual and expected lines, to be compared with expected
178+
// and actual lines for line to line comparison.
179+
enum Buffer<'a> {
180+
CommonLineBuffer(Vec<&'a str>),
181+
}
134182

135-
for line in &common_lines[0..COMMON_LINES_CONTEXT_SIZE] {
136-
write!(&mut truncated_lines, "\n{}", LineStyle::unchanged_style().style(line)).unwrap();
183+
impl<'a> Buffer<'a> {
184+
fn flush(&mut self, writer: impl std::fmt::Write) -> std::fmt::Result {
185+
match self {
186+
Buffer::CommonLineBuffer(common_lines) => {
187+
Self::flush_common_lines(std::mem::take(common_lines), writer)?
188+
}
189+
};
190+
Ok(())
137191
}
138192

139-
write!(
140-
&mut truncated_lines,
141-
"\n{}",
142-
LineStyle::comment_style().style(&format!(
143-
"<---- {} common lines omitted ---->",
144-
common_lines.len() - 2 * COMMON_LINES_CONTEXT_SIZE
145-
)),
146-
)
147-
.unwrap();
148-
149-
for line in &common_lines[common_lines.len() - COMMON_LINES_CONTEXT_SIZE..common_lines.len()] {
150-
write!(&mut truncated_lines, "\n{}", LineStyle::unchanged_style().style(line)).unwrap();
193+
fn flush_common_lines(
194+
common_lines: Vec<&'a str>,
195+
mut writer: impl std::fmt::Write,
196+
) -> std::fmt::Result {
197+
// The number of the lines kept before and after the compressed lines.
198+
const COMMON_LINES_CONTEXT_SIZE: usize = 2;
199+
200+
if common_lines.len() <= 2 * COMMON_LINES_CONTEXT_SIZE + 1 {
201+
for line in common_lines {
202+
write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
203+
}
204+
return Ok(());
205+
}
206+
207+
let start_context = &common_lines[0..COMMON_LINES_CONTEXT_SIZE];
208+
209+
for line in start_context {
210+
write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
211+
}
212+
213+
write!(
214+
writer,
215+
"\n{}",
216+
LineStyle::comment_style().style(&format!(
217+
"<---- {} common lines omitted ---->",
218+
common_lines.len() - 2 * COMMON_LINES_CONTEXT_SIZE
219+
)),
220+
)?;
221+
222+
let end_context =
223+
&common_lines[common_lines.len() - COMMON_LINES_CONTEXT_SIZE..common_lines.len()];
224+
225+
for line in end_context {
226+
write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
227+
}
228+
Ok(())
151229
}
152-
truncated_lines
153230
}
154231

155232
// Use ANSI code to enable styling on the summary lines.

0 commit comments

Comments
 (0)