diff --git a/CHANGELOG.md b/CHANGELOG.md index 824aeb4..e305c0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,75 @@ and this project adheres to ## [Unreleased] +## [0.5.1] - 2026-06-24 + +Bug-fix release addressing four formatter defects (five +code-level fixes) surfaced during the 0.5.0 adoption pass +across `senzing-commons-java` and `sz-sdk-java`. All changes +are formatter output fixes; no spec changes. + +### Fixed + +- **Mid-statement line comments dropped in + `variable_declarator`.** Comments positioned between `=` + and the value RHS (e.g. javadoc `// @highlight region="x"` + snippet markers on text-block assignments) were silently + dropped by the AST walk, breaking `mvn javadoc:javadoc` + under JDK 21 when the unpaired `@end region` closers + failed validation. Fix: source-preserve the `= ...` region + verbatim when mid-statement comments are detected, + matching the 0.5.0 treatment of comments inside arg lists. +- **Trailing whitespace bypassed `Emitter.newline()`'s + `rstrip` in source-preserve paths.** `write_raw_lines` + intentionally preserves trailing whitespace for text-block + content but had no opt-in for source-preserved CODE. + Fix: added `strip_trailing_ws` parameter; all + source-preserve-code call sites (conditions, arg lists, + formal parameters, non-javadoc block comments) pass `True`. +- **Array initializer missing space before `{`.** Produced + `new Type[]{ X }` instead of canonical `new Type[] { X }` + for some inputs; idempotent on both forms, so the file + accumulated mixed styles. Fix: emit a single space before + `array_initializer` children of + `array_creation_expression`. +- **Item-8 invariant not enforced for multi-arg arg lists.** + Calls of the shape + `assertEquals(arg1, longCallThatWraps(...), msg)` jammed + the third argument onto the wrapped second argument's + tail line. The 0.5.0 spec described this as + "width-gate handles it implicitly" but the gate misses + cases where the line happens to fit under 80 chars by + coincidence. Fix: explicit + "previous-arg-multi-row → break before next arg" check in + both `emit_p1` and `emit_p2_greedy`. +- **Binary positional arg ignored arg-start column.** When + a multi-arg call's positional argument was a + `binary_expression`, the binary's continuation operators + landed at `block + 4` instead of paren-aligning under the + argument's first operand column. Fix: set + `paren_align_col` to the arg's start column for the + duration of a binary-typed positional arg's emit. Narrow + to direct `binary_expression` (no paren unwrap) to avoid + the idempotency drift that originally narrowed 0.5.0 + item 10 to single-arg. + +### Verification + +- 656 formatter tests pass (was 645 at 0.5.0; +10 new + fixtures across `comment_preservation/`, `arg_list_wrap/`, + and `array_initializer/`). The first 6 lock the headline + fixes; the remaining 4 cover edge cases surfaced during the + PR review pass (block-comment between `=` and value, + array initializer without `new`, last-arg-multi-row in arg + lists, idempotency lock for the P4 paren-aligned shape). +- `senzing-commons-java` reformat: 2151 / 2151 tests pass, + `mvn -Pcheckstyle validate` BUILD SUCCESS, idempotent on + 2nd pass, zero trailing whitespace in source, array + initializers normalized. +- The sz-sdk-java demo files containing javadoc `@snippet` + markers no longer lose their `// @highlight` openers; + `mvn javadoc:javadoc` under JDK 21 succeeds. + ## [0.5.0] - 2026-06-23 Expands the formatter from the diff --git a/docs/faqs/building/consumer-trial-checklist.md b/docs/faqs/building/consumer-trial-checklist.md new file mode 100644 index 0000000..3a7d646 --- /dev/null +++ b/docs/faqs/building/consumer-trial-checklist.md @@ -0,0 +1,171 @@ +# Consumer Trial Checklist (Before Tagging a Standards Release) + +## Overview + +When preparing to tag a new release of `java-coding-standards`, +trial the candidate against each known adopter before the tag +lands. The shallow gate that 0.5.0 used (`mvn -Pcheckstyle +validate` only) missed a silent data-loss bug because checkstyle +doesn't validate that javadoc snippet markup survives +reformatting. The full gate below catches that class of issue. + +## The five gates + +A consumer trial PASSES only when ALL five gates pass: + +### 1. Checkstyle + +```bash +mvn -Pcheckstyle validate +``` + +Must report `BUILD SUCCESS` with zero LineLength or style +violations. If the consumer has source files that overflow at +the canonical column under the new spec's no-fallback policy, +the developer manually splits the long literal before this +gate goes green. + +### 2. Tests + +```bash +mvn test +``` + +Must report `BUILD SUCCESS` with zero failures, zero errors. +A formatter change that breaks tests is a semantic regression +(rare but possible — e.g. annotation arg order, string +escapes). + +### 3. Javadoc — never skip + +```bash +mvn javadoc:javadoc # adopt the consumer's default profile +``` + +Must report `BUILD SUCCESS`. **Run this with the consumer's +default profile, NOT a profile that strips javadoc snippet +markup.** + +The sz-sdk-java `java-17` profile sets +`x` on the +`maven-javadoc-plugin`, which strips `@snippet` tags entirely +under JDK 17 — useful for back-compat, but masks the entire +class of bug where the formatter drops `// @highlight` / +`// @end` snippet markers. The 0.5.0 release went out with a +real data-loss bug because the trial only ran the JDK-17 +profile. + +For consumers that target JDK 17+ for javadoc, run the gate +under the profile that DOES include snippet markup (typically +`java-18+`, `java-21`, or no profile at all). Pre-JDK-18 consumers +can use a plain javadoc invocation — the gate is checking +that the formatter didn't drop tokens, not that the resulting +javadoc renders correctly under every JDK. + +### 4. Token round-trip + +Count source tokens that the formatter could plausibly drop, +pre- and post-format. Counts must match: + +```bash +# Snippet markers (javadoc @snippet markup) +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + src/main/java -r > /tmp/pre-counts.txt +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + src/demo/java -r > /tmp/pre-demo-counts.txt + +# Trailing whitespace (forbidden per spec) +grep -rl ' $' src/main/java > /tmp/pre-trailing.txt + +# Format, then re-count +python3 .java-coding-standards/tooling/scripts/format_file.py \ + src/main/java src/test/java src/demo/java +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + src/main/java -r > /tmp/post-counts.txt +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + src/demo/java -r > /tmp/post-demo-counts.txt +grep -rl ' $' src/main/java > /tmp/post-trailing.txt + +diff /tmp/pre-counts.txt /tmp/post-counts.txt # must be empty +diff /tmp/pre-demo-counts.txt /tmp/post-demo-counts.txt # must be empty +diff /tmp/post-trailing.txt /dev/null # must be empty +``` + +If any diff is non-empty: the formatter is dropping or +introducing tokens — file a regression before tagging. + +### 5. Idempotency + +```bash +python3 .java-coding-standards/tooling/scripts/format_file.py \ + src/main/java src/test/java src/demo/java +``` + +Second invocation must report `0 modified`. A formatter that +produces different output on the second pass is non-idempotent +— file the case as a regression and don't tag. + +## What changed in 0.5.0 → 0.5.1 + +0.5.0's pre-release trial against sz-sdk-java ran ONLY gate 1 +(checkstyle). 0.5.0 shipped with a bug that: + +- Silently dropped `// @highlight region="..."` line comments + positioned between `=` and a text-block opener on assignment + statements (e.g. `String x = // @highlight\n"""...""";`). +- Was idempotent on the broken output (so the data loss was + unrecoverable by re-running the formatter). +- Was invisible to checkstyle (no rule covers token + preservation). +- Was invisible to `mvn javadoc:javadoc` under the consumer's + `java-17` profile (snippet tags were stripped by the profile + anyway). +- Surfaced when CI ran the `java-21` profile, which doesn't + strip snippets — javadoc validation failed on unpaired + `@end region` markers. + +Gates 3 and 4 above are designed to catch this and similar +data-loss bugs at consumer-trial time, before the standards +release is tagged. Run them. + +## Workflow + +```bash +# Setup: clone consumer with the candidate standards pin +cd /path/to/consumer +git checkout -b standards-trial-X.Y.Z +git -C .java-coding-standards fetch +git -C .java-coding-standards checkout FETCH_HEAD + +# Run all five gates +mvn -Pcheckstyle validate # gate 1 +mvn test # gate 2 +mvn javadoc:javadoc -P # gate 3 + +# Pre-format snapshot +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + $(find src -name '*.java') > /tmp/pre-tokens.txt + +# Format +python3 .java-coding-standards/tooling/scripts/format_file.py \ + src/main/java src/test/java src/demo/java + +# Gate 4 — token round-trip +grep -cE '(@highlight|@end|@start|@link|@replace)' \ + $(find src -name '*.java') > /tmp/post-tokens.txt +diff /tmp/pre-tokens.txt /tmp/post-tokens.txt # must be empty + +# Gate 5 — idempotency +python3 .java-coding-standards/tooling/scripts/format_file.py \ + src/main/java src/test/java src/demo/java +# Must report "0 modified" +``` + +If any gate fails, file the case as a regression PR against +the standards repo and re-run the trial after the fix. Don't +tag until every trial passes every gate. + +## See also + +- [Java formatting standards](java-formatting-standards.md) +- [Javadoc reflow conventions](javadoc-reflow-conventions.md) diff --git a/tooling/scripts/format_java.py b/tooling/scripts/format_java.py index 49f74b4..484139c 100644 --- a/tooling/scripts/format_java.py +++ b/tooling/scripts/format_java.py @@ -340,7 +340,7 @@ def newline(self) -> None: Trailing spaces on the finalized line are stripped before commit so emitters need not pre-trim them. """ - self._lines.append(self._current.rstrip(" ")) + self._lines.append(self._current.rstrip(" \t")) self._current = "" @property @@ -461,38 +461,42 @@ def last_lines_max_width(self, since: int) -> int: m = len(self._current) return m - def write_raw_lines(self, text: str) -> None: + def write_raw_lines( + self, text: str, *, strip_trailing_ws: bool = False + ) -> None: """Append text that may contain newlines, preserved verbatim. Used by leaf emitters for content the formatter must reproduce byte-for-byte — text blocks ("Text Blocks / Content preservation" spec section) and eventually block - comments. Newlines inside `text` finalize each intermediate - line WITHOUT stripping trailing whitespace, since that + comments. With the default `strip_trailing_ws=False`, + newlines inside `text` finalize each intermediate line + WITHOUT stripping trailing whitespace, since that whitespace is the developer's content (the spec's "Normalize spacing or alignment of content is a no-op" rule applies to text-block contents). + Source-preserved CODE (argument-list verbatim preservation, + mid-statement-comment preservation, etc.) is not + whitespace-significant content — trailing whitespace + there is just stray bytes the source author left behind + and the spec's "Trailing Whitespace" rule applies. Such + callers pass `strip_trailing_ws=True` to apply the same + `rstrip(" \t")` that `newline()` does on its finalized + lines. + The in-progress line at the END of `text` (the part after the last newline) is left open so subsequent `write()` / - `newline()` calls continue normally. Note: trailing - whitespace that the DEVELOPER wrote at the very end of a - text block (after the final newline, before any - formatter-emitted continuation) will be stripped by the - eventual `newline()` / `finish()` — that case doesn't - arise in well-formed Java source because every - `string_literal` ends with a non-whitespace closing - quote token, so the final segment passed here is never a - bare-whitespace string. Future emitters that pass other - kinds of verbatim multi-line content should guarantee the - same invariant. + `newline()` calls continue normally. """ parts = text.split("\n") # First segment continues the current line. self._current += parts[0] for part in parts[1:]: - # Each intermediate line is verbatim — NO strip. - self._lines.append(self._current) + if strip_trailing_ws: + self._lines.append(self._current.rstrip(" \t")) + else: + self._lines.append(self._current) self._current = part def push_indent(self) -> None: @@ -521,7 +525,7 @@ def finish(self) -> bytes: for files with at least one byte of real content. """ if self._current: - self._lines.append(self._current.rstrip(" ")) + self._lines.append(self._current.rstrip(" \t")) self._current = "" if not self._lines: return b"" @@ -3398,7 +3402,11 @@ def _emit_comment( if text.startswith("/**"): _emit_javadoc_block(emitter, source, node, text) return - emitter.write_raw_lines(text) + # Non-javadoc block comments (`/* … */`) are source-preserved + # but trailing whitespace inside them is never intentional + # alignment (any deliberate ASCII-art alignment would be + # inside a CSOFF region with its own preservation path). + emitter.write_raw_lines(text, strip_trailing_ws=True) _LINE_COMMENT_DIRECTIVE_PREFIXES: Final[tuple[str, ...]] = ( @@ -3828,7 +3836,9 @@ def _emit_array_initializer( re-emits with the normalized spacing. """ if _node_spans_multiple_rows(node): - emitter.write_raw_lines(_node_source_text(source, node)) + emitter.write_raw_lines( + _node_source_text(source, node), strip_trailing_ws=True + ) return elements = [c for c in node.named_children] if not elements: @@ -3854,10 +3864,20 @@ def _emit_array_creation_expression( optionally followed by an `array_initializer`. """ if _node_spans_multiple_rows(node): - emitter.write_raw_lines(_node_source_text(source, node)) + emitter.write_raw_lines( + _node_source_text(source, node), strip_trailing_ws=True + ) return emitter.write("new ") for child in node.named_children: + # Spec "Whitespace and Operator Spacing": single space + # before the opening `{` of an array initializer that + # follows `[]` (or `[N]`) dimensions. `new Type[] { X }` + # is canonical; `new Type[]{ X }` is a 0.5.0 bug where + # the emitter walked `dimensions` then `array_initializer` + # back-to-back without inserting the required separator. + if child.type == "array_initializer": + emitter.write(" ") _emit_node(emitter, source, child) @@ -3989,7 +4009,9 @@ def _emit_switch_rule( more statements. Source-preservation for multi-row source. """ if _node_spans_multiple_rows(node): - emitter.write_raw_lines(_node_source_text(source, node)) + emitter.write_raw_lines( + _node_source_text(source, node), strip_trailing_ws=True + ) return label = None body_children: list[Node] = [] @@ -4206,7 +4228,9 @@ def _emit_synchronized_statement( ) emitter.write("synchronized ") if _node_spans_multiple_rows(cond): - emitter.write_raw_lines(_node_source_text(source, cond)) + emitter.write_raw_lines( + _node_source_text(source, cond), strip_trailing_ws=True + ) emitter.newline() emitter.write_indent() _emit_node(emitter, source, body) @@ -4818,7 +4842,10 @@ def _emit_while_statement( if _node_spans_multiple_rows(condition): # Preserve the developer-authored multi-line condition # verbatim from source; switch to Allman brace. - emitter.write_raw_lines(_node_source_text(source, condition)) + emitter.write_raw_lines( + _node_source_text(source, condition), + strip_trailing_ws=True, + ) emitter.newline() emitter.write_indent() _emit_node(emitter, source, body) @@ -6067,7 +6094,9 @@ def _emit_formal_parameters( if not force_wrap and _node_spans_multiple_rows(node): # Preserve developer-authored multi-line params from # source. Includes opening `(` and closing `)`. - emitter.write_raw_lines(_node_source_text(source, node)) + emitter.write_raw_lines( + _node_source_text(source, node), strip_trailing_ws=True + ) return params = [ c for c in node.children @@ -6743,7 +6772,9 @@ def _emit_argument_list( if comments_present or _is_inside_csoff_region( source, node ): - emitter.write_raw_lines(src_text) + emitter.write_raw_lines( + src_text, strip_trailing_ws=True + ) return if emitter.paren_align_col is not None: target_col = emitter.paren_align_col + 4 @@ -6829,7 +6860,9 @@ def _emit_argument_list( "indent within the line limit." ), )) - emitter.write_raw_lines("\n".join(final_lines)) + emitter.write_raw_lines( + "\n".join(final_lines), strip_trailing_ws=True + ) return # Source-preserved first line wouldn't fit and there # are no comments — fall through. The wrap engine @@ -6864,6 +6897,39 @@ def _emit_argument_list( and args[0].type == "binary_expression" ) + def _emit_arg_with_optional_paren_align(arg: Node) -> None: + # Binary positional arg paren-align: when a positional + # arg is a binary expression, set `paren_align_col` to + # the arg's start column so the binary's continuation + # operators paren-align under the arg's first operand + # instead of falling back to the `block + 4` + # cumulative indent. This produces: + # + # assertTrue(cond, + # "msg " + # + var + # + " more"); + # + # instead of: + # + # assertTrue(cond, + # "msg " + # + var + " more"); // `+` at col 8 + # + # Narrow to direct binary_expression (no paren unwrap) + # to avoid the idempotency drift that originally + # narrowed the single-arg call-paren extension to + # binary args only. + if arg.type == "binary_expression": + arg_col = emitter.column + prev_align = emitter.set_paren_align_col(arg_col) + try: + _emit_node(emitter, source, arg) + finally: + emitter.set_paren_align_col(prev_align) + else: + _emit_node(emitter, source, arg) + def emit_p1() -> None: emitter.write("(") if single_arg_binary: @@ -6874,10 +6940,29 @@ def emit_p1() -> None: finally: emitter.set_paren_align_col(prev_align) else: + cont_col = emitter.column + prev_arg_multi_row = False for index, arg in enumerate(args): if index > 0: - emitter.write(", ") - _emit_node(emitter, source, arg) + if prev_arg_multi_row: + # Item 8 invariant in arg-list P1: + # when the previous arg emitted + # multi-row (a nested call / lambda / + # binary wrapped), break before this + # arg so it doesn't jam onto the + # wrapped construct's tail line. The + # break lands at the call's post-`(` + # column. + emitter.write(",") + emitter.newline() + emitter.write(" " * cont_col) + else: + emitter.write(", ") + operand_start = emitter.line_count + _emit_arg_with_optional_paren_align(arg) + prev_arg_multi_row = ( + emitter.line_count > operand_start + ) emitter.write(")") def emit_p4_single_arg() -> None: @@ -6914,13 +6999,37 @@ def emit_p2_greedy() -> None: emitter.write("(") cont_col = emitter.column effective_max = _MAX_LINE - emitter.tail_reserve + prev_arg_multi_row = False for index, arg in enumerate(args): if index == 0: - _emit_node(emitter, source, arg) + operand_start = emitter.line_count + _emit_arg_with_optional_paren_align(arg) + prev_arg_multi_row = ( + emitter.line_count > operand_start + ) + continue + if prev_arg_multi_row: + # Item 8 invariant for arg lists: the previous + # arg's emission introduced newlines (a nested + # call / lambda / binary wrapped multi-row), + # so force break before this arg. Otherwise + # the next arg lands at whatever column the + # prior arg's wrap tail ended on, jamming + # `arg)` onto the same line as the wrapped + # construct's closing. + emitter.write(",") + emitter.newline() + emitter.write(" " * cont_col) + operand_start = emitter.line_count + _emit_arg_with_optional_paren_align(arg) + prev_arg_multi_row = ( + emitter.line_count > operand_start + ) continue saved = emitter.snapshot() emitter.write(", ") - _emit_node(emitter, source, arg) + operand_start = emitter.line_count + _emit_arg_with_optional_paren_align(arg) widths_ok = ( emitter.last_lines_max_width(saved[0]) <= effective_max @@ -6952,7 +7061,11 @@ def emit_p2_greedy() -> None: emitter.write(",") emitter.newline() emitter.write(" " * cont_col) - _emit_node(emitter, source, arg) + operand_start = emitter.line_count + _emit_arg_with_optional_paren_align(arg) + prev_arg_multi_row = ( + emitter.line_count > operand_start + ) emitter.write(")") def emit_p4_multi_arg() -> None: @@ -6973,12 +7086,14 @@ def emit_p4_multi_arg() -> None: emitter.write(")") emitter.pop_indent() - # P1 (single line) is always tried first. The wrap engine - # measures actual rendered widths via try_priorities, so a - # multi-line arg (lambda body, nested wrapping call) that - # blows past 80 chars during P1 emit simply falls through - # to the next candidate. Letting P1 try also keeps the - # decision deterministic from the AST — earlier code + # P1 is the AST-deterministic single-line candidate, but + # may emit a multi-row layout when an intermediate arg + # wraps multi-row and item-8 forces a break before + # subsequent args. try_priorities still + # measures actual rendered widths, so a P1 emit that + # blows past 80 chars falls through to the next + # candidate. Letting P1 try keeps the decision + # deterministic from the AST — earlier code # short-circuited P1 when any arg's SOURCE was multi-row, # which made the decision flip between formatter passes. candidates: list[Callable[[], None]] = [emit_p1] @@ -7648,6 +7763,44 @@ def _emit_variable_declarator( break if value is None: return + # Mid-statement comment preservation: when + # line_comment / block_comment "extras" sit between the + # `=` token and the value RHS (e.g. javadoc + # `// @highlight region="..."` snippet markers on + # assignment-with-text-block), the wrap engine has no way + # to represent comments inline with operator placement, + # so source-preserve the entire `= ...` region verbatim. + # This matches the treatment of comments inside argument + # lists (`_arg_list_takes_source_preserve_path`). Without + # this guard the comments are silently dropped + # since `_emit_node(value)` walks only the value subtree + # and never visits the extra comment children. + equals_token = None + for child in node.children: + if child.type == "=": + equals_token = child + break + if equals_token is not None: + mid_comments = [ + c for c in node.children + if c.type in ("line_comment", "block_comment") + and c.start_byte > equals_token.start_byte + and c.start_byte < value.start_byte + ] + if mid_comments: + # Emit `= `. + # Verbatim preserves the developer's whitespace + # between `=`, the comments, and the value — the + # only safe transform when comment placement + # carries semantic meaning (snippet markers). + emitter.write(" ") + verbatim = source[ + equals_token.start_byte:value.end_byte + ].decode("utf-8") + emitter.write_raw_lines( + verbatim, strip_trailing_ws=True + ) + return # Wrap-priority for assignment: prefer the cleanest single- # line form over wrapping the value internally. Order: # diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/expected.java b/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/expected.java new file mode 100644 index 0000000..c6b5032 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/expected.java @@ -0,0 +1,10 @@ +public class Demo +{ + void run(String expectedText, String result, String jsonValue) + { + assertEquals(expectedText.replaceAll("\\s", ""), result.replaceAll( + "\\s", + ""), + "Unexpected pretty-print result: " + jsonValue); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/input.java b/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/input.java new file mode 100644 index 0000000..8626470 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/04_item8_prev_arg_multi_row_breaks_next/input.java @@ -0,0 +1,7 @@ +public class Demo +{ + void run(String expectedText, String result, String jsonValue) + { + assertEquals(expectedText.replaceAll("\\s", ""), result.replaceAll("\\s", ""), "Unexpected pretty-print result: " + jsonValue); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/expected.java b/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/expected.java new file mode 100644 index 0000000..1c9a118 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/expected.java @@ -0,0 +1,8 @@ +public class Demo +{ + void check(boolean cond, int bytes, int available) + { + assertTrue(available < bytes, "More bytes available than should be (" + + bytes + "): " + available); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/input.java b/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/input.java new file mode 100644 index 0000000..f1361fc --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/05_paren_align_binary_positional_arg/input.java @@ -0,0 +1,7 @@ +public class Demo +{ + void check(boolean cond, int bytes, int available) + { + assertTrue(available < bytes, "More bytes available than should be (" + bytes + "): " + available); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/expected.java b/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/expected.java new file mode 100644 index 0000000..ebbf615 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/expected.java @@ -0,0 +1,9 @@ +public class Demo +{ + void run(String first, String second) + { + assertEquals("expected", actualMethod.replaceAll("\\s", "") + .replaceAll("\\n", " ") + .trim()); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/input.java b/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/input.java new file mode 100644 index 0000000..e3d3764 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/06_last_arg_multi_row/input.java @@ -0,0 +1,7 @@ +public class Demo +{ + void run(String first, String second) + { + assertEquals("expected", actualMethod.replaceAll("\\s", "").replaceAll("\\n", " ").trim()); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/expected.java b/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/expected.java new file mode 100644 index 0000000..1c9a118 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/expected.java @@ -0,0 +1,8 @@ +public class Demo +{ + void check(boolean cond, int bytes, int available) + { + assertTrue(available < bytes, "More bytes available than should be (" + + bytes + "): " + available); + } +} diff --git a/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/input.java b/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/input.java new file mode 100644 index 0000000..1c9a118 --- /dev/null +++ b/tooling/scripts/tests/fixtures/arg_list_wrap/07_paren_align_binary_idempotency_lock/input.java @@ -0,0 +1,8 @@ +public class Demo +{ + void check(boolean cond, int bytes, int available) + { + assertTrue(available < bytes, "More bytes available than should be (" + + bytes + "): " + available); + } +} diff --git a/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/expected.java b/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/expected.java new file mode 100644 index 0000000..5659429 --- /dev/null +++ b/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/expected.java @@ -0,0 +1,6 @@ +public class Demo +{ + Class[] types = new Class[] { Connection.class }; + Object[] values = new Object[] { a, b }; + String[] empty = new String[] {}; +} diff --git a/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/input.java b/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/input.java new file mode 100644 index 0000000..895f8d0 --- /dev/null +++ b/tooling/scripts/tests/fixtures/array_initializer/01_space_before_brace_after_dimensions/input.java @@ -0,0 +1,6 @@ +public class Demo +{ + Class[] types = new Class[]{ Connection.class }; + Object[] values = new Object[]{ a, b }; + String[] empty = new String[]{}; +} diff --git a/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/expected.java b/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/expected.java new file mode 100644 index 0000000..17f6a93 --- /dev/null +++ b/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/expected.java @@ -0,0 +1,5 @@ +public class Demo +{ + int[] numbers = { 1, 2, 3 }; + String[] empty = {}; +} diff --git a/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/input.java b/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/input.java new file mode 100644 index 0000000..17f6a93 --- /dev/null +++ b/tooling/scripts/tests/fixtures/array_initializer/02_initializer_without_new/input.java @@ -0,0 +1,5 @@ +public class Demo +{ + int[] numbers = { 1, 2, 3 }; + String[] empty = {}; +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/expected.java b/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/expected.java new file mode 100644 index 0000000..b458e4f --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/expected.java @@ -0,0 +1,16 @@ +public class Demo +{ + void run() + { + // get a record definition (varies by application) + String recordDefinition = // @highlight substring="recordDefinition" + // @highlight type="italic" region="recordDefinition" + """ + { + "DATA_SOURCE": "TEST", + "RECORD_ID": "ABC123" + } + """; + // @end region="recordDefinition" + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/input.java b/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/input.java new file mode 100644 index 0000000..b458e4f --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/01_assignment_text_block_with_snippet_markers/input.java @@ -0,0 +1,16 @@ +public class Demo +{ + void run() + { + // get a record definition (varies by application) + String recordDefinition = // @highlight substring="recordDefinition" + // @highlight type="italic" region="recordDefinition" + """ + { + "DATA_SOURCE": "TEST", + "RECORD_ID": "ABC123" + } + """; + // @end region="recordDefinition" + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/expected.java b/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/expected.java new file mode 100644 index 0000000..2a4977e --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/expected.java @@ -0,0 +1,8 @@ +public class Demo +{ + void run() + { + String greeting = // explanatory note + "hello, world"; + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/input.java b/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/input.java new file mode 100644 index 0000000..2a4977e --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/02_assignment_string_literal_with_comment/input.java @@ -0,0 +1,8 @@ +public class Demo +{ + void run() + { + String greeting = // explanatory note + "hello, world"; + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/expected.java b/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/expected.java new file mode 100644 index 0000000..0e6626e --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/expected.java @@ -0,0 +1,16 @@ +public class Demo +{ + void run() + { + // @start region="example" + String recordDefinition = // @highlight region="recordDefinition" + """ + { + "DATA_SOURCE": "TEST" + } + """; + // @end region="recordDefinition" + System.out.println(recordDefinition); + // @end region="example" + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/input.java b/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/input.java new file mode 100644 index 0000000..0e6626e --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/03_snippet_region_round_trip/input.java @@ -0,0 +1,16 @@ +public class Demo +{ + void run() + { + // @start region="example" + String recordDefinition = // @highlight region="recordDefinition" + """ + { + "DATA_SOURCE": "TEST" + } + """; + // @end region="recordDefinition" + System.out.println(recordDefinition); + // @end region="example" + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/expected.java b/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/expected.java new file mode 100644 index 0000000..2daefb5 --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/expected.java @@ -0,0 +1,7 @@ +public class Demo +{ + void run() + { + String label = /* descriptive block comment */ "hello"; + } +} diff --git a/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/input.java b/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/input.java new file mode 100644 index 0000000..2daefb5 --- /dev/null +++ b/tooling/scripts/tests/fixtures/comment_preservation/04_block_comment_between_eq_and_value/input.java @@ -0,0 +1,7 @@ +public class Demo +{ + void run() + { + String label = /* descriptive block comment */ "hello"; + } +}