Skip to content

Commit 5196e92

Browse files
authored
Merge pull request #113 from shadr/improve-tests
Check idempotence in tests
2 parents bc70b20 + b8a8783 commit 5196e92

File tree

6 files changed

+131
-69
lines changed

6 files changed

+131
-69
lines changed

queries/gdscript.scm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@
192192
(comment)
193193
(region_start)
194194
(region_end)
195-
(annotation)] @allow_blank_line_before)
195+
(annotation)
196+
(function_definition)
197+
(class_definition)
198+
(constructor_definition)] @allow_blank_line_before)
196199

197200
(setget) @prepend_indent_start @append_indent_end
198201
(setget ":" @prepend_antispace)

src/formatter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ impl Formatter {
166166
// - must contain at least one new line character between `extends_name` and optional doc comment
167167
// - may contain multiple doc comment lines that starts with `##` and ends with a new line character
168168
let re = RegexBuilder::new(
169-
r#"(?P<extends_line>^extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n+(?P<doc>(?:^##.*\n)*)\n*(?P<EOF>\z)?"#,
169+
r#"(?P<extends_line>^extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n+((?P<doc>(?:^##.*\n)+)(?:\z|\n))?\n*(?P<EOF>\z)?"#,
170170
)
171171
.multi_line(true)
172172
.build()

src/reorder.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,13 @@ fn extract_tokens_to_reorder(
252252
// We need to check if the comments are contiguous with the declaration they are
253253
// attached to.
254254
let mut class_docstring_comments = Vec::new();
255-
let mut found_non_comment_non_class = false;
255+
let mut class_docstring_comments_rows = Vec::new();
256256
for (node, text) in &all_nodes {
257257
match node.kind() {
258258
"comment" => {
259-
if text.trim_start().starts_with("##") && !found_non_comment_non_class {
259+
if text.trim_start().starts_with("##") {
260260
class_docstring_comments.push(text.clone());
261+
class_docstring_comments_rows.push(node.start_position().row);
261262
}
262263
}
263264
"class_name_statement" | "extends_statement" | "annotation" => {
@@ -266,7 +267,24 @@ fn extract_tokens_to_reorder(
266267
// Any other element means we're past the top of the file, so we stop
267268
// collecting the class docstring
268269
_ => {
269-
found_non_comment_non_class = true;
270+
// if the last node of the docstring is immediately followed by the current node
271+
if class_docstring_comments_rows
272+
.last()
273+
.is_some_and(|row| row + 1 == node.start_position().row)
274+
{
275+
// count how many rows are belong to the statement docstring
276+
let statement_docstring_rows = class_docstring_comments_rows
277+
.iter()
278+
.rev()
279+
.zip((0..).map(|i| class_docstring_comments_rows.last().unwrap() - i))
280+
.take_while(|(actual, expected)| **actual == *expected)
281+
.count();
282+
class_docstring_comments
283+
.truncate(class_docstring_comments.len() - statement_docstring_rows);
284+
class_docstring_comments_rows
285+
.truncate(class_docstring_comments_rows.len() - statement_docstring_rows);
286+
}
287+
break;
270288
}
271289
}
272290
}
@@ -304,7 +322,7 @@ fn extract_tokens_to_reorder(
304322
"comment" => {
305323
// We already processed class docstring comments, so we skip them here
306324
// This may look inefficient but in practice it should not have much impact
307-
if text.trim_start().starts_with("##") && class_docstring_comments.contains(&text) {
325+
if class_docstring_comments_rows.contains(&node.start_position().row) {
308326
continue;
309327
} else {
310328
pending_comments.push(text);
Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
1-
class_name Player
21
extends CharacterBody2D
3-
## A brief description of the class's role and functionality.
2+
## Blankline after docstring
43
##
54
## The description of script
65

7-
class_name Player
6+
var a = 10
87
extends CharacterBody2D
9-
## A brief description of the class's role and functionality.
8+
## Blankline before and after docstring
109
##
1110
## The description of script
1211

13-
class_name Player
12+
var a = 10
1413
extends CharacterBody2D
15-
## A brief description of the class's role and functionality.
14+
15+
## Blankline before docstring
1616
##
17-
## The description of script
17+
## The description of a variable
18+
var a = 10
19+
extends CharacterBody2D
1820

21+
## No blankline around docstring
22+
##
23+
## The description of a variable
1924
var a = 10
20-
class_name Player
2125
extends CharacterBody2D
22-
## A brief description of the class's role and functionality.
26+
27+
## Blanklink before function docstring
28+
##
29+
## The description of a variable
30+
func foo():
31+
pass
32+
extends CharacterBody2D
33+
## Blankline between docstring and function definition
2334
##
24-
## The description of script
35+
## The description of a variable
36+
37+
func foo():
38+
pass
39+
extends CharacterBody2D
2540

2641
var a = 10
27-
class_name Player
2842
extends CharacterBody2D
29-
## A brief description of the class's role and functionality.
3043

3144
var a = 10
45+
extends CharacterBody2D
46+
## Docstring at the end of file
47+
##
48+
## The description of script

tests/input/class_doc_comment.gd

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,47 @@
1-
class_name Player extends CharacterBody2D
2-
3-
## A brief description of the class's role and functionality.
4-
##
5-
## The description of script
6-
class_name Player extends CharacterBody2D
7-
## A brief description of the class's role and functionality.
1+
extends CharacterBody2D
2+
## Blankline after docstring
83
##
94
## The description of script
10-
class_name Player extends CharacterBody2D
11-
## A brief description of the class's role and functionality.
5+
6+
var a = 10
7+
extends CharacterBody2D
8+
9+
## Blankline before and after docstring
1210
##
1311
## The description of script
1412

1513
var a = 10
16-
class_name Player
1714
extends CharacterBody2D
1815

19-
## A brief description of the class's role and functionality.
16+
## Blankline before docstring
2017
##
21-
## The description of script
18+
## The description of a variable
19+
var a = 10
20+
extends CharacterBody2D
21+
## No blankline around docstring
22+
##
23+
## The description of a variable
24+
var a = 10
25+
extends CharacterBody2D
26+
## Blanklink before function docstring
27+
##
28+
## The description of a variable
29+
func foo():
30+
pass
31+
extends CharacterBody2D
32+
## Blankline between docstring and function definition
33+
##
34+
## The description of a variable
35+
36+
func foo():
37+
pass
38+
extends CharacterBody2D
2239

2340
var a = 10
24-
class_name Player
2541
extends CharacterBody2D
26-
## A brief description of the class's role and functionality.
2742
var a = 10
43+
44+
extends CharacterBody2D
45+
## Docstring at the end of file
46+
##
47+
## The description of script

tests/integration_tests.rs

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use gdscript_formatter::FormatterConfig;
2-
use gdscript_formatter::formatter::{format_gdscript, format_gdscript_with_config};
2+
use gdscript_formatter::formatter::format_gdscript_with_config;
33
use similar::{ChangeTag, TextDiff};
44
use std::fs;
55
use std::path::Path;
@@ -13,13 +13,15 @@ fn make_whitespace_visible(s: &str) -> String {
1313
.replace('\n', "↲\n")
1414
}
1515

16-
fn assert_formatted_eq(result: &str, expected: &str, file_path: &Path) {
16+
fn assert_formatted_eq(
17+
result: &str,
18+
expected: &str,
19+
file_path: &Path,
20+
error_context_message: &str,
21+
) {
1722
if result != expected {
18-
eprintln!(
19-
"\nFormatted output doesn't match expected for {}",
20-
file_path.display()
21-
);
22-
eprintln!("Diff between expected(-) and formatted output(+):");
23+
eprintln!("\n{} - {}", error_context_message, file_path.display());
24+
eprintln!("Diff between expected(-) and actual output(+):");
2325
let diff = TextDiff::from_lines(expected, result);
2426
for change in diff.iter_all_changes() {
2527
let text = make_whitespace_visible(&change.to_string());
@@ -34,34 +36,26 @@ fn assert_formatted_eq(result: &str, expected: &str, file_path: &Path) {
3436
eprintln!("{:?}", expected);
3537
eprintln!("\nGOT (raw):");
3638
eprintln!("{:?}", result);
37-
panic!("Assertion failed");
39+
panic!("Assertion failed: {}", error_context_message);
3840
}
3941
}
4042

4143
fn test_file(file_path: &Path) {
42-
let file_name = file_path.file_name().expect("path is not a file path");
43-
44-
let input_path = file_path;
45-
let expected_path = file_path
46-
.parent()
47-
.unwrap()
48-
.parent()
49-
.unwrap()
50-
.join("expected/")
51-
.join(file_name);
52-
53-
let input_content =
54-
fs::read_to_string(&input_path).expect(&format!("Failed to read {}", input_path.display()));
55-
let expected_content = fs::read_to_string(&expected_path)
56-
.expect(&format!("Failed to read {}", expected_path.display()));
57-
58-
let result = format_gdscript(&input_content)
59-
.expect(&format!("Failed to format {}", input_path.display()));
60-
61-
assert_formatted_eq(&result, &expected_content, &input_path);
44+
test_file_with_config(file_path, &FormatterConfig::default(), true);
6245
}
6346

6447
fn test_reorder_file(file_path: &Path) {
48+
test_file_with_config(
49+
file_path,
50+
&FormatterConfig {
51+
reorder_code: true,
52+
..Default::default()
53+
},
54+
true,
55+
);
56+
}
57+
58+
fn test_file_with_config(file_path: &Path, config: &FormatterConfig, check_idempotence: bool) {
6559
let file_name = file_path.file_name().expect("path is not a file path");
6660

6761
let input_path = file_path;
@@ -78,14 +72,24 @@ fn test_reorder_file(file_path: &Path) {
7872
let expected_content = fs::read_to_string(&expected_path)
7973
.expect(&format!("Failed to read {}", expected_path.display()));
8074

81-
let result = format_gdscript_with_config(
82-
&input_content,
83-
&FormatterConfig {
84-
reorder_code: true,
85-
..Default::default()
86-
},
87-
)
88-
.expect(&format!("Failed to format {}", input_path.display()));
75+
let result = format_gdscript_with_config(&input_content, config)
76+
.expect(&format!("Failed to format {}", input_path.display()));
77+
78+
assert_formatted_eq(
79+
&result,
80+
&expected_content,
81+
&input_path,
82+
"First formatting output doesn't match expected",
83+
);
8984

90-
assert_formatted_eq(&result, &expected_content, &input_path);
85+
if check_idempotence {
86+
let second_result = format_gdscript_with_config(&result, config)
87+
.expect(&format!("Failed to format {}", input_path.display()));
88+
assert_formatted_eq(
89+
&second_result,
90+
&result,
91+
&input_path,
92+
"Idempotence check failed, formatting a second time gave different results",
93+
);
94+
}
9195
}

0 commit comments

Comments
 (0)