From fd920b7d5233ae9a01fe0ceda2c376bef4c9462d Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Fri, 30 Jul 2021 18:43:10 +0200 Subject: [PATCH] Remove \r when running PipeTest on Windows --- test/net/sourceforge/plantuml/PipeTest.java | 738 ++++++++++---------- 1 file changed, 387 insertions(+), 351 deletions(-) diff --git a/test/net/sourceforge/plantuml/PipeTest.java b/test/net/sourceforge/plantuml/PipeTest.java index 35f1c1cd3..1ac074284 100644 --- a/test/net/sourceforge/plantuml/PipeTest.java +++ b/test/net/sourceforge/plantuml/PipeTest.java @@ -1,6 +1,5 @@ package net.sourceforge.plantuml; - import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -20,356 +19,393 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; - class PipeTest { - ByteArrayOutputStream baos; - ErrorStatus errorStatus; - Option option; - Pipe pipe; - PrintStream ps; - - @BeforeEach - void setup() { - errorStatus = ErrorStatus.init(); - - baos = new ByteArrayOutputStream(); - option = new Option(); - ps = new PrintStream(baos); - - pipe = new Pipe(option, ps, new ByteArrayInputStream(new byte[0]), UTF_8.toString()); - } - - @Test - void should_managePipe_set_as_no_error_for_empty_input() throws IOException { - pipe = new Pipe(option, ps, new ByteArrayInputStream(new byte[0]), UTF_8.name()); - - pipe.managePipe(errorStatus); - - try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { - softly.assertThat(errorStatus.hasError()).isFalse(); - softly.assertThat(errorStatus.isNoData()).isTrue(); - softly.assertThat(baos.toByteArray()).isEmpty(); - } - } - - - static List managePipeTestCases() { - LinkedList l = new LinkedList<>(); - - l.add(TestCase.of("", "", "", Verification.EXACT, false, true)); - - // ok render - l.add(TestCase.of("", "a->b", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, false, false)); - l.add(TestCase.of("", "@startuml\na->b\n@enduml", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, false, false)); - l.add(TestCase.of("", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", "(?s).*(PNG.*Generated by http://plantuml.com.*){2}", Verification.REGEX, false, false)); - - // bad data - l.add(TestCase.of("", "a", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, true, false)); - l.add(TestCase.of("", "@startuml\na\n@enduml\n", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, true, false)); - l.add(TestCase.of("", "@startuml\na\n@enduml\n@startuml\na\n@enduml\n", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, true, false)); - - // ignore garbage before; after; before&between&after diagrams - l.add(TestCase.of("", "this-is-garbage\n@startuml\na->b\n@enduml", "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, false, false)); - l.add(TestCase.of("", "@startuml\na->b\n@enduml\nthis-is-garbage", "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, false, false)); - l.add(TestCase.of("", "this-is-garbage\n@startuml\na->b\n@enduml\nthis-is-garbage\n@startuml\na->b\n@enduml\nthis-is-garbage", "(?s).*(PNG.*Generated by http://plantuml.com.*){2}", Verification.REGEX, false, false)); - - // ignore other diagram start tags when still not closed (but fails to generate) - l.add(TestCase.of("", "@startuml\na->b\n@startgantt\n@enduml\n", "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, true, false)); - - // manage @@@format svg - l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml", "(?s).*<\\?xml.*", Verification.REGEX, false, false)); - l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\n@@@format svg\na->b\n@enduml", "(?s).*(<\\?xml.*.*){2}", Verification.REGEX, false, false)); - - // mixed formats - l.add(TestCase.of("", "@startuml\n@@@format png\na->b\n@enduml\n@startuml\n@@@format svg\na->b\n@enduml", "(?s).*PNG.*Generated by http://plantuml.com.*<\\?xml.*", Verification.REGEX, false, false)); - l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\n@@@format png\na->b\n@enduml", "(?s).*<\\?xml.*.*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, false, false)); - - // pipe delimitor - l.add(TestCase.of("-pipedelimitor PIPE-DELIMITOR", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", "(?s).*(PNG.*Generated by http://plantuml.com.*PIPE-DELIMITOR.*){2}", Verification.REGEX, false, false)); - - // if format is set in first diagram and not in the second, it will be used in both (Possibly incorrect: preseved from old behaviour) - l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\na->b\n@enduml", "(?s).*(<\\?xml.*.*){2}", Verification.REGEX, false, false)); - - // ok computeurl - l.add(TestCase.of("-computeurl", "@startuml\na->b\n@enduml", "IzIrIm80\n", Verification.EXACT, false, true)); - l.add(TestCase.of("-computeurl", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", "IzIrIm80\nIzIrI-9AqhLB1W00\n", Verification.EXACT,false, true)); - - // ok encodeurl - l.add(TestCase.of("-encodeurl", "@startuml\na->b\n@enduml", "IzIrIm80\n", Verification.EXACT,false, true)); - l.add(TestCase.of("-encodeurl", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", "IzIrIm80\nIzIrI-9AqhLB1W00\n", Verification.EXACT, false, true)); - - // valid syntax - l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml", "SEQUENCE\n(2 participants)\n", Verification.EXACT, false, false)); - l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", "SEQUENCE\n(2 participants)\nSEQUENCE\n(3 participants)\n", Verification.EXACT, false, false)); - l.add(TestCase.of("-syntax", "@startgantt\n[a] lasts 1 day\n@endgantt", "OTHER\n(Project)\n", Verification.EXACT, false, false)); - - // invalid syntax - l.add(TestCase.of("-syntax", "@startuml\na\n@enduml", "ERROR\n1\nSyntax Error?\n", Verification.EXACT, true, false)); - l.add(TestCase.of("-syntax", "@startuml\na\n@enduml\n@startuml\na\n@enduml", "ERROR\n1\nSyntax Error?\nERROR\n1\nSyntax Error?\n", Verification.EXACT, true, false)); - l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml\n@startuml\na\n@enduml", "SEQUENCE\n(2 participants)\nERROR\n1\nSyntax Error?\n", Verification.EXACT, true, false)); - l.add(TestCase.of("-syntax", "@startuml\na\n@enduml\n@startuml\na->b\n@enduml", "ERROR\n1\nSyntax Error?\nSEQUENCE\n(2 participants)\n", Verification.EXACT, true, false)); - - // pipemap (using regexp to allow any coords so that it doesn't fail on different systems) - l.add(TestCase.of("-pipemap", "@startuml\na->b: [[http://a.com]] c\n@enduml", - "\n" + - "\"\"\n" + - "\n\n", - Verification.REGEX, false, false)); - l.add(TestCase.of("-pipemap", "@startuml\na->b: [[http://a.com]] c\n@enduml\n@startuml\nc->d: [[http://c.com]] e\n@enduml", - "\n" + - "\"\"\n" + - "\n\n" + - "\n" + - "\"\"\n" + - "\n\n", - Verification.REGEX, false, false)); - - // no links/invalid input => no pipemap to output (no error as of https://forum.plantuml.net/10049/2019-pipemap-diagrams-containing-links-give-zero-exit-code ) - l.add(TestCase.of("-pipemap", "@startuml\na->b\n@enduml", "\n", Verification.EXACT, false, false)); - l.add(TestCase.of("-pipemap", "@startuml\na\n@enduml", "\n", Verification.EXACT, false, false)); - - return l; - } - @ParameterizedTest - @MethodSource("managePipeTestCases") - void should_managePipe_manage_success_cases_correctly(TestCase testCase) throws IOException, InterruptedException { - option = new Option(testCase.getOptions().split(" ")); - pipe = new Pipe(option, ps, new ByteArrayInputStream(testCase.getInput().getBytes(UTF_8)), UTF_8.name()); - - pipe.managePipe(errorStatus); - - try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { - softly.assertThat(errorStatus.hasError()).isEqualTo(testCase.isExpectedHasErrors()); - softly.assertThat(errorStatus.isNoData()).isEqualTo(testCase.isExpectedIsNoData()); - switch(testCase.getExpectedOutVerification()) { - case EXACT: - softly.assertThat(new String(baos.toByteArray(), UTF_8)).isEqualTo(testCase.getExpectedOut()); - break; - - case REGEX: - softly.assertThat(new String(baos.toByteArray(), UTF_8)).matches(testCase.getExpectedOut()); - break; - } - } - } - - @Test - void should_readFirstDiagram_return_null_for_empty_input() throws IOException { - pipe = new Pipe(option, null, new ByteArrayInputStream(new byte[0]), UTF_8.name()); - - String actual = pipe.readFirstDiagram(); - - assertThat(actual).isNull(); - } - - @Test - void should_readFirstDiagram_decode_a_special_unicode_character_when_provided_charset_is_utf8() throws IOException { - pipe = new Pipe(option, null, new ByteArrayInputStream("\u2620\n".getBytes(UTF_8)), UTF_8.name()); - - String actual = pipe.readFirstDiagram(); - - assertThat(actual).isEqualTo("@startuml\n\u2620\n@enduml\n"); - } - - // The testing is relevant only if the testing VM is configured in non-UTF-8 defaultCharset() - @Test - void should_readFirstDiagram_uses_current_charset_if_not_provided() throws IOException { - String input = "\u00C1"; // A acute accented. (HTML Á). Multibyte in UTF-8. - if(Charset.defaultCharset().newEncoder().canEncode(input)) { - pipe = new Pipe(option, null, new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), null); - - String actual = pipe.readFirstDiagram(); - - assertThat(actual).isEqualTo("@startuml\n\u00C1\n@enduml\n"); - - } else { - // default charset can't encode Á. Ignore the test. - assertTrue(true); - } - } - - @ParameterizedTest - @ValueSource(strings = { - "ab\nc", // *nix, macOsX - "ab\rc", // pre-macOsX macs - "ab\r\nc", // Windows - // the case \n\r is handled as 2 new lines, thus not added - - "ab\nc\n", - "ab\nc\r", - "ab\nc\r\n" - }) - void should_readFirstDiagram_decode_correctly_different_line_endings(String input) throws IOException { - pipe = new Pipe(option, null, new ByteArrayInputStream(input.getBytes(UTF_8)), UTF_8.name()); - - String first = pipe.readFirstDiagram(); - String second = pipe.readFirstDiagram(); - - assertThat(first).isEqualTo("@startuml\nab\nc\n@enduml\n"); - assertThat(second).isEqualTo(null); // no spurious diagram afterwards - } - - static List firstStartAndEndMarks() { - List l = new LinkedList<>(); - l.add(InputExpected.of("\nab\r\ncde", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("\nab\r\ncde\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n@enduml\n")); - l.add(InputExpected.of("\nab\r\ncde\n@endwhatever", "@startuml\nab\ncde\n@endwhatever\n@enduml\n")); - l.add(InputExpected.of("\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\r\n\r\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("this-is-garbage\n@startuml\nab\rcde\n@enduml\nthis-is-garbage\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startwhatever\nab\rcde\n@endwhatever", "@startwhatever\nab\ncde\n@endwhatever\n")); - return l; - } - - @ParameterizedTest - @MethodSource("firstStartAndEndMarks") - void should_readFirstDiagram_handle_correctly_start_and_end_marks(InputExpected inputExpected) throws IOException { - pipe = new Pipe(option, null, new ByteArrayInputStream(inputExpected.getInput().getBytes(UTF_8)), UTF_8.name()); - - String actual = pipe.readFirstDiagram(); - - assertThat(actual).isEqualTo(inputExpected.getExpected()); - } - - static List subsequentStartAndEndMarks() { - List l = new LinkedList<>(); - l.add(InputExpected.of("\nab\r\ncde", null)); - l.add(InputExpected.of("\nab\r\ncde\n", null)); - l.add(InputExpected.of("\nab\r\ncde\n@enduml", null)); - l.add(InputExpected.of("\nab\r\ncde\n@endwhatever", null)); - l.add(InputExpected.of("\nab\r\ncde\n@enduml\n", null)); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\r\n\r\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("this-is-garbage\n@startuml\nab\rcde\n@enduml\nthis-is-garbage\n", "@startuml\nab\ncde\n@enduml\n")); - l.add(InputExpected.of("@startwhatever\nab\rcde\n@endwhatever", "@startwhatever\nab\ncde\n@endwhatever\n")); - return l; - } - - @ParameterizedTest - @MethodSource("subsequentStartAndEndMarks") - void should_readSubsequentDiagram_handle_correctly_start_and_end_marks(InputExpected inputExpected) throws IOException { - pipe = new Pipe(option, null, new ByteArrayInputStream(inputExpected.getInput().getBytes(UTF_8)), UTF_8.name()); - - String actual = pipe.readSubsequentDiagram(); - - assertThat(actual).isEqualTo(inputExpected.getExpected()); - } - - @ParameterizedTest - @ValueSource(strings = {"@@@format png", "png", "@@@format png "}) - void should_manageFormat_handle_png(String valid) { - pipe.manageFormat(valid); - - assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.PNG); - } - - @ParameterizedTest - @ValueSource(strings = {"@@@format svg", "svg", "@@@format svg "}) - void should_manageFormat_handle_svg(String valid) { - pipe.manageFormat(valid); - - assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.SVG); - } - - @ParameterizedTest - @ValueSource(strings = {"@@@format invalid", "invalid", "@@@format invalid "}) - void should_manageFormat_ignore_any_other_value_and_keep_default_value_as_file_format_option(String valid) { - pipe.manageFormat(valid); - - assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.PNG); - } - - static class TestCase { - - private final String options; - private final String input; - private final String expectedOut; - private final Verification expectedOutVerification; - private final boolean expectedHasErrors; - private final boolean expectedIsNoData; - - - public TestCase(String options, String input, String expectedOut, Verification expectedOutVerification, boolean expectedHasErrors, boolean expectedIsNoData) { - this.options = options; - this.input = input; - this.expectedOut = expectedOut; - this.expectedOutVerification = expectedOutVerification; - this.expectedHasErrors = expectedHasErrors; - this.expectedIsNoData = expectedIsNoData; - } - - public static TestCase of(String option, String input, String expectedOut, Verification expectedOutVerification, boolean expectedHasErrors, boolean expectedIsNoData) { - return new TestCase(option, input, expectedOut, expectedOutVerification, expectedHasErrors, expectedIsNoData); - } - - public String getOptions() { - return options; - } - - public String getInput() { - return input; - } - - public String getExpectedOut() { - return expectedOut; - } - - public Verification getExpectedOutVerification() { - return expectedOutVerification; - } - - public boolean isExpectedHasErrors() { - return expectedHasErrors; - } - - public boolean isExpectedIsNoData() { - return expectedIsNoData; - } - - @Override - public String toString() { - return "o:'" + options + "', i:'" + input + "', e-out='" + expectedOut + "', e-err='" + expectedHasErrors + "', e-nodata='" + expectedIsNoData + "'"; - } - - } - - static class InputExpected { - private final String input; - private final String expected; - - - public InputExpected(String input, String expected) { - this.input = input; - this.expected = expected; - } - - public static InputExpected of(String input, String expected) { - return new InputExpected(input, expected); - } - - public String getInput() { - return input; - } - - public String getExpected() { - return expected; - } - - @Override - public String toString() { - return "i:'" + input + "', e='" + expected + "'"; - } - } - - enum Verification { - EXACT, - REGEX - } + ByteArrayOutputStream baos; + ErrorStatus errorStatus; + Option option; + Pipe pipe; + PrintStream ps; + + @BeforeEach + void setup() { + errorStatus = ErrorStatus.init(); + + baos = new ByteArrayOutputStream(); + option = new Option(); + ps = new PrintStream(baos); + + pipe = new Pipe(option, ps, new ByteArrayInputStream(new byte[0]), UTF_8.toString()); + } + + @Test + void should_managePipe_set_as_no_error_for_empty_input() throws IOException { + pipe = new Pipe(option, ps, new ByteArrayInputStream(new byte[0]), UTF_8.name()); + + pipe.managePipe(errorStatus); + + try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(errorStatus.hasError()).isFalse(); + softly.assertThat(errorStatus.isNoData()).isTrue(); + softly.assertThat(baos.toByteArray()).isEmpty(); + } + } + + static List managePipeTestCases() { + LinkedList l = new LinkedList<>(); + + l.add(TestCase.of("", "", "", Verification.EXACT, false, true)); + + // ok render + l.add(TestCase.of("", "a->b", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, false, + false)); + l.add(TestCase.of("", "@startuml\na->b\n@enduml", "(?s).*PNG.*Generated by http://plantuml.com.*", + Verification.REGEX, false, false)); + l.add(TestCase.of("", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", + "(?s).*(PNG.*Generated by http://plantuml.com.*){2}", Verification.REGEX, false, false)); + + // bad data + l.add(TestCase.of("", "a", "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, true, false)); + l.add(TestCase.of("", "@startuml\na\n@enduml\n", "(?s).*PNG.*Generated by http://plantuml.com.*", + Verification.REGEX, true, false)); + l.add(TestCase.of("", "@startuml\na\n@enduml\n@startuml\na\n@enduml\n", + "(?s).*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, true, false)); + + // ignore garbage before; after; before&between&after diagrams + l.add(TestCase.of("", "this-is-garbage\n@startuml\na->b\n@enduml", + "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, false, false)); + l.add(TestCase.of("", "@startuml\na->b\n@enduml\nthis-is-garbage", + "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, false, false)); + l.add(TestCase.of("", + "this-is-garbage\n@startuml\na->b\n@enduml\nthis-is-garbage\n@startuml\na->b\n@enduml\nthis-is-garbage", + "(?s).*(PNG.*Generated by http://plantuml.com.*){2}", Verification.REGEX, false, false)); + + // ignore other diagram start tags when still not closed (but fails to generate) + l.add(TestCase.of("", "@startuml\na->b\n@startgantt\n@enduml\n", + "(?s).*(PNG.*Generated by http://plantuml.com.*){1}", Verification.REGEX, true, false)); + + // manage @@@format svg + l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml", "(?s).*<\\?xml.*", + Verification.REGEX, false, false)); + l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\n@@@format svg\na->b\n@enduml", + "(?s).*(<\\?xml.*.*){2}", Verification.REGEX, false, false)); + + // mixed formats + l.add(TestCase.of("", "@startuml\n@@@format png\na->b\n@enduml\n@startuml\n@@@format svg\na->b\n@enduml", + "(?s).*PNG.*Generated by http://plantuml.com.*<\\?xml.*", Verification.REGEX, false, + false)); + l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\n@@@format png\na->b\n@enduml", + "(?s).*<\\?xml.*.*PNG.*Generated by http://plantuml.com.*", Verification.REGEX, false, + false)); + + // pipe delimitor + l.add(TestCase.of("-pipedelimitor PIPE-DELIMITOR", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", + "(?s).*(PNG.*Generated by http://plantuml.com.*PIPE-DELIMITOR.*){2}", Verification.REGEX, false, + false)); + + // if format is set in first diagram and not in the second, it will be used in + // both (Possibly incorrect: preseved from old behaviour) + l.add(TestCase.of("", "@startuml\n@@@format svg\na->b\n@enduml\n@startuml\na->b\n@enduml", + "(?s).*(<\\?xml.*.*){2}", Verification.REGEX, false, false)); + + // ok computeurl + l.add(TestCase.of("-computeurl", "@startuml\na->b\n@enduml", "IzIrIm80\n", Verification.EXACT, false, true)); + l.add(TestCase.of("-computeurl", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", + "IzIrIm80\nIzIrI-9AqhLB1W00\n", Verification.EXACT, false, true)); + + // ok encodeurl + l.add(TestCase.of("-encodeurl", "@startuml\na->b\n@enduml", "IzIrIm80\n", Verification.EXACT, false, true)); + l.add(TestCase.of("-encodeurl", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", + "IzIrIm80\nIzIrI-9AqhLB1W00\n", Verification.EXACT, false, true)); + + // valid syntax + l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml", "SEQUENCE\n(2 participants)\n", Verification.EXACT, + false, false)); + l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml\n@startuml\na->b\nb->c\n@enduml\n", + "SEQUENCE\n(2 participants)\nSEQUENCE\n(3 participants)\n", Verification.EXACT, false, false)); + l.add(TestCase.of("-syntax", "@startgantt\n[a] lasts 1 day\n@endgantt", "OTHER\n(Project)\n", + Verification.EXACT, false, false)); + + // invalid syntax + l.add(TestCase.of("-syntax", "@startuml\na\n@enduml", "ERROR\n1\nSyntax Error?\n", Verification.EXACT, true, + false)); + l.add(TestCase.of("-syntax", "@startuml\na\n@enduml\n@startuml\na\n@enduml", + "ERROR\n1\nSyntax Error?\nERROR\n1\nSyntax Error?\n", Verification.EXACT, true, false)); + l.add(TestCase.of("-syntax", "@startuml\na->b\n@enduml\n@startuml\na\n@enduml", + "SEQUENCE\n(2 participants)\nERROR\n1\nSyntax Error?\n", Verification.EXACT, true, false)); + l.add(TestCase.of("-syntax", "@startuml\na\n@enduml\n@startuml\na->b\n@enduml", + "ERROR\n1\nSyntax Error?\nSEQUENCE\n(2 participants)\n", Verification.EXACT, true, false)); + + // pipemap (using regexp to allow any coords so that it doesn't fail on + // different systems) + l.add(TestCase.of("-pipemap", "@startuml\na->b: [[http://a.com]] c\n@enduml", + "\n" + + "\"\"\n" + + "\n\n", + Verification.REGEX, false, false)); + l.add(TestCase.of("-pipemap", + "@startuml\na->b: [[http://a.com]] c\n@enduml\n@startuml\nc->d: [[http://c.com]] e\n@enduml", + "\n" + + "\"\"\n" + + "\n\n" + "\n" + + "\"\"\n" + + "\n\n", + Verification.REGEX, false, false)); + + // no links/invalid input => no pipemap to output (no error as of + // https://forum.plantuml.net/10049/2019-pipemap-diagrams-containing-links-give-zero-exit-code + // ) + l.add(TestCase.of("-pipemap", "@startuml\na->b\n@enduml", "\n", Verification.EXACT, false, false)); + l.add(TestCase.of("-pipemap", "@startuml\na\n@enduml", "\n", Verification.EXACT, false, false)); + + return l; + } + + @ParameterizedTest + @MethodSource("managePipeTestCases") + void should_managePipe_manage_success_cases_correctly(TestCase testCase) throws IOException, InterruptedException { + option = new Option(testCase.getOptions().split(" ")); + pipe = new Pipe(option, ps, new ByteArrayInputStream(testCase.getInput().getBytes(UTF_8)), UTF_8.name()); + + pipe.managePipe(errorStatus); + + try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(errorStatus.hasError()).isEqualTo(testCase.isExpectedHasErrors()); + softly.assertThat(errorStatus.isNoData()).isEqualTo(testCase.isExpectedIsNoData()); + testCase.getExpectedOutVerification().assertOk(softly, baos, testCase.getExpectedOut()); + } + } + + @Test + void should_readFirstDiagram_return_null_for_empty_input() throws IOException { + pipe = new Pipe(option, null, new ByteArrayInputStream(new byte[0]), UTF_8.name()); + + String actual = pipe.readFirstDiagram(); + + assertThat(actual).isNull(); + } + + @Test + void should_readFirstDiagram_decode_a_special_unicode_character_when_provided_charset_is_utf8() throws IOException { + pipe = new Pipe(option, null, new ByteArrayInputStream("\u2620\n".getBytes(UTF_8)), UTF_8.name()); + + String actual = pipe.readFirstDiagram(); + + assertThat(actual).isEqualTo("@startuml\n\u2620\n@enduml\n"); + } + + // The testing is relevant only if the testing VM is configured in non-UTF-8 + // defaultCharset() + @Test + void should_readFirstDiagram_uses_current_charset_if_not_provided() throws IOException { + String input = "\u00C1"; // A acute accented. (HTML Á). Multibyte in UTF-8. + if (Charset.defaultCharset().newEncoder().canEncode(input)) { + pipe = new Pipe(option, null, new ByteArrayInputStream(input.getBytes(Charset.defaultCharset())), null); + + String actual = pipe.readFirstDiagram(); + + assertThat(actual).isEqualTo("@startuml\n\u00C1\n@enduml\n"); + + } else { + // default charset can't encode Á. Ignore the test. + assertTrue(true); + } + } + + @ParameterizedTest + @ValueSource(strings = { "ab\nc", // *nix, macOsX + "ab\rc", // pre-macOsX macs + "ab\r\nc", // Windows + // the case \n\r is handled as 2 new lines, thus not added + + "ab\nc\n", "ab\nc\r", "ab\nc\r\n" }) + void should_readFirstDiagram_decode_correctly_different_line_endings(String input) throws IOException { + pipe = new Pipe(option, null, new ByteArrayInputStream(input.getBytes(UTF_8)), UTF_8.name()); + + String first = pipe.readFirstDiagram(); + String second = pipe.readFirstDiagram(); + + assertThat(first).isEqualTo("@startuml\nab\nc\n@enduml\n"); + assertThat(second).isEqualTo(null); // no spurious diagram afterwards + } + + static List firstStartAndEndMarks() { + List l = new LinkedList<>(); + l.add(InputExpected.of("\nab\r\ncde", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("\nab\r\ncde\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n@enduml\n")); + l.add(InputExpected.of("\nab\r\ncde\n@endwhatever", "@startuml\nab\ncde\n@endwhatever\n@enduml\n")); + l.add(InputExpected.of("\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\r\n\r\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("this-is-garbage\n@startuml\nab\rcde\n@enduml\nthis-is-garbage\n", + "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startwhatever\nab\rcde\n@endwhatever", "@startwhatever\nab\ncde\n@endwhatever\n")); + return l; + } + + @ParameterizedTest + @MethodSource("firstStartAndEndMarks") + void should_readFirstDiagram_handle_correctly_start_and_end_marks(InputExpected inputExpected) throws IOException { + pipe = new Pipe(option, null, new ByteArrayInputStream(inputExpected.getInput().getBytes(UTF_8)), UTF_8.name()); + + String actual = pipe.readFirstDiagram(); + + assertThat(actual).isEqualTo(inputExpected.getExpected()); + } + + static List subsequentStartAndEndMarks() { + List l = new LinkedList<>(); + l.add(InputExpected.of("\nab\r\ncde", null)); + l.add(InputExpected.of("\nab\r\ncde\n", null)); + l.add(InputExpected.of("\nab\r\ncde\n@enduml", null)); + l.add(InputExpected.of("\nab\r\ncde\n@endwhatever", null)); + l.add(InputExpected.of("\nab\r\ncde\n@enduml\n", null)); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\n\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startuml\nab\r\ncde\n@enduml\r\n\r\n", "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("this-is-garbage\n@startuml\nab\rcde\n@enduml\nthis-is-garbage\n", + "@startuml\nab\ncde\n@enduml\n")); + l.add(InputExpected.of("@startwhatever\nab\rcde\n@endwhatever", "@startwhatever\nab\ncde\n@endwhatever\n")); + return l; + } + + @ParameterizedTest + @MethodSource("subsequentStartAndEndMarks") + void should_readSubsequentDiagram_handle_correctly_start_and_end_marks(InputExpected inputExpected) + throws IOException { + pipe = new Pipe(option, null, new ByteArrayInputStream(inputExpected.getInput().getBytes(UTF_8)), UTF_8.name()); + + String actual = pipe.readSubsequentDiagram(); + + assertThat(actual).isEqualTo(inputExpected.getExpected()); + } + + @ParameterizedTest + @ValueSource(strings = { "@@@format png", "png", "@@@format png " }) + void should_manageFormat_handle_png(String valid) { + pipe.manageFormat(valid); + + assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.PNG); + } + + @ParameterizedTest + @ValueSource(strings = { "@@@format svg", "svg", "@@@format svg " }) + void should_manageFormat_handle_svg(String valid) { + pipe.manageFormat(valid); + + assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.SVG); + } + + @ParameterizedTest + @ValueSource(strings = { "@@@format invalid", "invalid", "@@@format invalid " }) + void should_manageFormat_ignore_any_other_value_and_keep_default_value_as_file_format_option(String valid) { + pipe.manageFormat(valid); + + assertThat(option.getFileFormatOption().getFileFormat()).isEqualTo(FileFormat.PNG); + } + + static class TestCase { + + private final String options; + private final String input; + private final String expectedOut; + private final Verification expectedOutVerification; + private final boolean expectedHasErrors; + private final boolean expectedIsNoData; + + public TestCase(String options, String input, String expectedOut, Verification expectedOutVerification, + boolean expectedHasErrors, boolean expectedIsNoData) { + this.options = options; + this.input = input; + this.expectedOut = expectedOut; + this.expectedOutVerification = expectedOutVerification; + this.expectedHasErrors = expectedHasErrors; + this.expectedIsNoData = expectedIsNoData; + } + + public static TestCase of(String option, String input, String expectedOut, Verification expectedOutVerification, + boolean expectedHasErrors, boolean expectedIsNoData) { + return new TestCase(option, input, expectedOut, expectedOutVerification, expectedHasErrors, + expectedIsNoData); + } + + public String getOptions() { + return options; + } + + public String getInput() { + return input; + } + + public String getExpectedOut() { + return expectedOut; + } + + public Verification getExpectedOutVerification() { + return expectedOutVerification; + } + + public boolean isExpectedHasErrors() { + return expectedHasErrors; + } + + public boolean isExpectedIsNoData() { + return expectedIsNoData; + } + + @Override + public String toString() { + return "o:'" + options + "', i:'" + input + "', e-out='" + expectedOut + "', e-err='" + expectedHasErrors + + "', e-nodata='" + expectedIsNoData + "'"; + } + + } + + static class InputExpected { + private final String input; + private final String expected; + + public InputExpected(String input, String expected) { + this.input = input; + this.expected = expected; + } + + public static InputExpected of(String input, String expected) { + return new InputExpected(input, expected); + } + + public String getInput() { + return input; + } + + public String getExpected() { + return expected; + } + + @Override + public String toString() { + return "i:'" + input + "', e='" + expected + "'"; + } + } + + enum Verification { + EXACT, REGEX; + + void assertOk(AutoCloseableSoftAssertions softly, ByteArrayOutputStream baos, String expectedOut) { + final String result = new String(baos.toByteArray(), UTF_8).replaceAll("\r", ""); + switch (this) { + case EXACT: + softly.assertThat(result).isEqualTo(expectedOut); + break; + + case REGEX: + softly.assertThat(result).matches(expectedOut); + break; + } + } + } }