test/jdk/sun/security/util/ManifestDigester/FindSection.java
changeset 57488 94691d8e746f
equal deleted inserted replaced
57487:643978a35f6e 57488:94691d8e746f
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.lang.reflect.Constructor;
       
    25 import java.lang.reflect.Field;
       
    26 import java.lang.reflect.Method;
       
    27 import java.util.Arrays;
       
    28 import java.util.concurrent.Callable;
       
    29 import java.util.function.Consumer;
       
    30 
       
    31 import sun.security.util.ManifestDigester;
       
    32 
       
    33 import org.testng.annotations.Test;
       
    34 import org.testng.annotations.BeforeClass;
       
    35 import org.testng.annotations.BeforeMethod;
       
    36 import org.testng.annotations.DataProvider;
       
    37 import org.testng.annotations.Factory;
       
    38 
       
    39 import static java.nio.charset.StandardCharsets.UTF_8;
       
    40 import static org.testng.Assert.*;
       
    41 
       
    42 /**
       
    43  * @test
       
    44  * @bug 8217375
       
    45  * @modules java.base/sun.security.util:+open
       
    46  * @compile ../../tools/jarsigner/Utils.java
       
    47  * @run testng/othervm FindSection
       
    48  * @summary Check {@link ManifestDigester#findSection}.
       
    49  */
       
    50 public class FindSection {
       
    51 
       
    52     /*
       
    53      * TODO:
       
    54      * FIXED_8217375 is not intended to keep. it is intended to show what
       
    55      * exactly has changed with respect to the previous version for which no
       
    56      * such test existed.
       
    57      */
       
    58     static final boolean FIXED_8217375 = true;
       
    59 
       
    60     /**
       
    61      * {@link ManifestDigester.Entry#digestWorkaround} should not feed the
       
    62      * trailing blank line into the digester. Before resolution of 8217375 it
       
    63      * fed the trailing blank line into the digest if the second line break
       
    64      * was at the end of the file due to <pre>
       
    65      * if (allBlank || (i == len-1)) {
       
    66      *     if (i == len-1)
       
    67      *         pos.endOfSection = i;
       
    68      *     else
       
    69      *         pos.endOfSection = last;
       
    70      * </pre> in {@link ManifestDigester#findSection}. In that case at the end
       
    71      * of the manifest file, {@link ManifestDigester.Entry#digestWorkaround}
       
    72      * would have produced the same digest as
       
    73      * {@link ManifestDigester.Entry#digest} which was wrong and without effect
       
    74      * at best.
       
    75      * <p>
       
    76      * Once this fix is accepted, this flag can be removed along with
       
    77      * {@link #actualEndOfSection8217375}.
       
    78      */
       
    79     static final boolean FIXED_8217375_EOF_ENDOFSECTION = FIXED_8217375;
       
    80 
       
    81     /**
       
    82      * {@link ManifestDigester.Position.endOfSection} usually points to the
       
    83      * start position of the blank line trailing a section minus one.
       
    84      * If a {@link ManifestDigester.Position} returned by
       
    85      * {@link ManifestDigester#findSection} is based on a portion that starts
       
    86      * with a blank line, above statement is (or was) not true, because of the
       
    87      * initialization of {@code last} in {@link ManifestDigester#findSection}
       
    88      * <pre>
       
    89      * int last = offset;
       
    90      * </pre>
       
    91      * which would point after incrementing it in {@code pos.endOfSection + 1}
       
    92      * on line 128 (line number before this change) or {@code int sectionLen =
       
    93      * pos.endOfSection-start+1;} on line 133 (line number before this change)
       
    94      * at one byte after the first line break character of usually two and
       
    95      * possibly (assuming "{@code \r\n}" default line break normally) in between
       
    96      * the two characters of a line break. After subtracting again the index of
       
    97      * the section start position on former line 133, the last byte would be
       
    98      * missed to be digested by {@link ManifestDigester.Entry#digestWorkaround}.
       
    99      * <p>
       
   100      * All this, however could possibly matter (or have mattered) only when
       
   101      * {@link ManifestDigester#findSection} was invoked with an offset position
       
   102      * pointing straight to a line break which happens if a manifest starts
       
   103      * with an empty line or if there are superfluous blank lines between
       
   104      * sections in both cases no useful manifest portion is identified.
       
   105      * Superfluous blank lines are not identified as sections (because they
       
   106      * don't have a name and specifically don't meet {@code if (len > 6) {} on
       
   107      * former line 136. Manifests starting with a line break are not any more
       
   108      * useful either.
       
   109      * <p>
       
   110      * Once this fix is accepted, this flag can be removed along with
       
   111      * {@link #actualEndOfSection8217375}.
       
   112      */
       
   113     static final boolean FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION =
       
   114             FIXED_8217375;
       
   115 
       
   116     static Constructor<?> PositionConstructor;
       
   117     static Method findSection;
       
   118     static Field rawBytes;
       
   119     static Field endOfFirstLine;
       
   120     static Field endOfSection;
       
   121     static Field startOfNext;
       
   122 
       
   123     @BeforeClass
       
   124     public static void setFindSectionAccessible() throws Exception {
       
   125         Class<?> Position = Arrays.stream(ManifestDigester.class.
       
   126                 getDeclaredClasses()).filter(c -> c.getSimpleName().
       
   127                         equals("Position")).findFirst().get();
       
   128         PositionConstructor = Position.getDeclaredConstructor();
       
   129         PositionConstructor.setAccessible(true);
       
   130         findSection = ManifestDigester.class.getDeclaredMethod("findSection",
       
   131             int.class, Position);
       
   132         findSection.setAccessible(true);
       
   133         rawBytes = ManifestDigester.class.getDeclaredField("rawBytes");
       
   134         rawBytes.setAccessible(true);
       
   135         endOfFirstLine = Position.getDeclaredField("endOfFirstLine");
       
   136         endOfFirstLine.setAccessible(true);
       
   137         endOfSection = Position.getDeclaredField("endOfSection");
       
   138         endOfSection.setAccessible(true);
       
   139         startOfNext = Position.getDeclaredField("startOfNext");
       
   140         startOfNext.setAccessible(true);
       
   141     }
       
   142 
       
   143     static class Position {
       
   144         final int endOfFirstLine; // not including newline character
       
   145 
       
   146         final int endOfSection; // end of section, not including the blank line
       
   147                                 // between sections
       
   148         final int startOfNext;  // the start of the next section
       
   149 
       
   150         Position(Object pos) throws ReflectiveOperationException {
       
   151             endOfFirstLine = FindSection.endOfFirstLine.getInt(pos);
       
   152             endOfSection = FindSection.endOfSection.getInt(pos);
       
   153             startOfNext = FindSection.startOfNext.getInt(pos);
       
   154         }
       
   155     }
       
   156 
       
   157     Position findSection(byte[] manifestBytes)
       
   158             throws ReflectiveOperationException {
       
   159         ManifestDigester manDig = new ManifestDigester("\n\n".getBytes(UTF_8));
       
   160         FindSection.rawBytes.set(manDig, manifestBytes);
       
   161         Object pos = PositionConstructor.newInstance();
       
   162         Object result = findSection.invoke(manDig, offset, pos);
       
   163         if (Boolean.FALSE.equals(result)) {
       
   164             return null; // indicates findSection having returned false
       
   165         } else {
       
   166             return new Position(pos);
       
   167         }
       
   168     }
       
   169 
       
   170     @DataProvider(name = "parameters")
       
   171     public static Object[][] parameters() {
       
   172         return new Object[][] { { 0 }, { 42 } };
       
   173     }
       
   174 
       
   175     @Factory(dataProvider = "parameters")
       
   176     public static Object[] createTests(int offset) {
       
   177         return new Object[]{ new FindSection(offset) };
       
   178     }
       
   179 
       
   180     final int offset;
       
   181 
       
   182     FindSection(int offset) {
       
   183         this.offset = offset;
       
   184     }
       
   185 
       
   186     @BeforeMethod
       
   187     public void verbose() {
       
   188         System.out.println("offset = " + offset);
       
   189     }
       
   190 
       
   191     Position findSection(String manifestString)
       
   192             throws ReflectiveOperationException {
       
   193         byte[] manifestBytes = manifestString.getBytes(UTF_8);
       
   194         byte[] manifestWithOffset = new byte[manifestBytes.length + offset];
       
   195         System.arraycopy(manifestBytes, 0, manifestWithOffset, offset,
       
   196                 manifestBytes.length);
       
   197         return findSection(manifestWithOffset);
       
   198     }
       
   199 
       
   200     /**
       
   201      * Surprising, but the offset actually makes a difference in
       
   202      * {@link ManifestDigester#findSection} return value.
       
   203      */
       
   204     @SuppressWarnings("unused")
       
   205     int actualEndOfFirstLine8217375(int correctPosition) {
       
   206         // if the parsed portion of the manifest starts with a blank line,
       
   207         // and offset is 0, "pos.endOfFirstLine = -1;" probably denoting a
       
   208         // yet uninitialized value coincides with the assignment by
       
   209         // "pos.endOfFirstLine = i-1;" if i == 0 and
       
   210         // "if (pos.endOfFirstLine == -1)" after "case '\n':" happens to
       
   211         // become true even though already assigned.
       
   212         if (offset == 0 && correctPosition == -1 && !FIXED_8217375) return 0;
       
   213         return correctPosition;
       
   214     }
       
   215 
       
   216     @SuppressWarnings("unused")
       
   217     int actualEndOfSection8217375(int correctPosition, boolean eof, int lbl) {
       
   218         // if the parsed portion of the manifest ends with a blank line and
       
   219         // just before eof, the blank line is included in Position.endOfSection/
       
   220         // Section.length (the one usually without blank line as well as in
       
   221         // Position.startOfNext/Section.lengthWithBlankLine) which is used
       
   222         // in digestWorkaround (independent of the digest without workaround)
       
   223         if (eof && !FIXED_8217375_EOF_ENDOFSECTION) {
       
   224             return correctPosition + lbl;
       
   225         } else if (correctPosition == -1
       
   226                 && !FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION) {
       
   227             return 0;
       
   228         } else {
       
   229             return correctPosition;
       
   230         }
       
   231     }
       
   232 
       
   233     AssertionError collectErrors(AssertionError a, Runnable run) {
       
   234         try {
       
   235             run.run();
       
   236         } catch (AssertionError e) {
       
   237             if (a == null) a = new AssertionError();
       
   238             a.addSuppressed(e);
       
   239         }
       
   240         return a;
       
   241     }
       
   242 
       
   243     void assertPosition(Position pos,
       
   244             int endOfFirstLine, int endOfSection, int startOfNext) {
       
   245         AssertionError a = null;
       
   246         a = collectErrors(a, () -> assertEquals(
       
   247                 pos.endOfFirstLine, endOfFirstLine + offset, "endOfFirstLine"));
       
   248         a = collectErrors(a, () -> assertEquals(
       
   249                 pos.endOfSection, endOfSection + offset, "endOfSection"));
       
   250         a = collectErrors(a, () -> assertEquals(
       
   251                 pos.startOfNext, startOfNext + offset, "startOfNext"));
       
   252         if (a != null) throw a;
       
   253     }
       
   254 
       
   255     void catchCrCausesIndexOutOfBoundsException(
       
   256             Callable<Position> test, Consumer<Position> asserts) {
       
   257         try {
       
   258             Position x = test.call();
       
   259             if (!FIXED_8217375) fail();
       
   260             asserts.accept(x);
       
   261         } catch (Exception e) {
       
   262             if (e instanceof IndexOutOfBoundsException ||
       
   263                 e.getCause() instanceof IndexOutOfBoundsException) {
       
   264                 if (FIXED_8217375) throw new AssertionError(e);
       
   265             } else {
       
   266                 throw new AssertionError(e);
       
   267             }
       
   268         }
       
   269     }
       
   270 
       
   271     @Test
       
   272     public void testEmpty() throws Exception {
       
   273         assertNull(findSection(""));
       
   274     }
       
   275 
       
   276     @Test
       
   277     public void testOneLineBreakCr() throws Exception {
       
   278         catchCrCausesIndexOutOfBoundsException(
       
   279                 () -> findSection("\r"),
       
   280                 p -> assertPosition(p,
       
   281                         -1, actualEndOfSection8217375(-1, false, 1), 1)
       
   282         );
       
   283     }
       
   284 
       
   285     @Test
       
   286     public void testOneLineBreakLf() throws Exception {
       
   287         assertPosition(findSection("\n"),
       
   288                 -1, actualEndOfSection8217375(-1, false, 1), 1);
       
   289     }
       
   290 
       
   291     @Test
       
   292     public void testOneLineBreakCrLf() throws Exception {
       
   293         assertPosition(findSection("\r\n"),
       
   294                 actualEndOfFirstLine8217375(-1),
       
   295                 actualEndOfSection8217375(-1, true, 2),
       
   296                 2);
       
   297     }
       
   298 
       
   299     @Test
       
   300     public void testSpaceAndLineBreakCr() throws Exception {
       
   301         catchCrCausesIndexOutOfBoundsException(
       
   302                 () -> findSection("   \r"),
       
   303                 p -> assertPosition(p, 2, 3, 4)
       
   304         );
       
   305     }
       
   306 
       
   307     @Test
       
   308     public void testSpaceAndOneLineBreakLf() throws Exception {
       
   309         assertPosition(findSection("   \n"), 2, 3, 4);
       
   310     }
       
   311 
       
   312     @Test
       
   313     public void testSpaceAndOneLineBreakCrLf() throws Exception {
       
   314         assertPosition(findSection("   \r\n"), 2, 4, 5);
       
   315     }
       
   316 
       
   317     @Test
       
   318     public void testOneLineBreakCrAndSpace() throws Exception {
       
   319         assertPosition(findSection("\r   "),
       
   320                 -1, actualEndOfSection8217375(-1, false, 1), 1);
       
   321     }
       
   322 
       
   323     @Test
       
   324     public void testOneLineBreakLfAndSpace() throws Exception {
       
   325         assertPosition(findSection("\n   "),
       
   326                 -1, actualEndOfSection8217375(-1, false, 1), 1);
       
   327     }
       
   328 
       
   329     @Test
       
   330     public void testOneLineBreakCrLfAndSpace() throws Exception {
       
   331         assertPosition(findSection("\r\n   "),
       
   332                 actualEndOfFirstLine8217375(-1),
       
   333                 actualEndOfSection8217375(-1, false, 1),
       
   334                 2);
       
   335     }
       
   336 
       
   337     @Test
       
   338     public void testCrEof() throws Exception {
       
   339         catchCrCausesIndexOutOfBoundsException(
       
   340                 () -> findSection("abc\r"),
       
   341                 p -> assertPosition(p, 2, 3, 4)
       
   342         );
       
   343     }
       
   344 
       
   345     @Test
       
   346     public void testLfEof() throws Exception {
       
   347         assertPosition(findSection("abc\n"), 2, 3, 4);
       
   348     }
       
   349 
       
   350     @Test
       
   351     public void testCrLfEof() throws Exception {
       
   352         assertPosition(findSection("abc\r\n"), 2, 4, 5);
       
   353     }
       
   354 
       
   355     @Test
       
   356     public void testCrContinued() throws Exception {
       
   357         assertPosition(findSection("abc\rxyz\r\n\r\n   "), 2, 8, 11);
       
   358     }
       
   359 
       
   360     @Test
       
   361     public void testLfContinued() throws Exception {
       
   362         assertPosition(findSection("abc\nxyz\r\n\r\n   "), 2, 8, 11);
       
   363     }
       
   364 
       
   365     @Test
       
   366     public void testCrLfContinued() throws Exception {
       
   367         assertPosition(findSection("abc\r\nxyz\r\n\r\n   "), 2, 9, 12);
       
   368     }
       
   369 
       
   370     @Test
       
   371     public void testCrCrEof() throws Exception {
       
   372         catchCrCausesIndexOutOfBoundsException(
       
   373                 () -> findSection("abc\r\nxyz\r\r"),
       
   374                 p -> assertPosition(p,
       
   375                         2, actualEndOfSection8217375(8, true, 1), 10)
       
   376         );
       
   377     }
       
   378 
       
   379     @Test
       
   380     public void testCrCrContinued() throws Exception {
       
   381         assertPosition(findSection("abc\r\nxyz\r\r   "), 2, 8, 10);
       
   382     }
       
   383 
       
   384     @Test
       
   385     public void testLfLfEof() throws Exception {
       
   386         assertPosition(findSection("abc\r\nxyz\n\n"),
       
   387                 2, actualEndOfSection8217375(8, true, 1), 10);
       
   388     }
       
   389 
       
   390     @Test
       
   391     public void testLfLfContinued() throws Exception {
       
   392         assertPosition(findSection("abc\r\nxyz\n\n   "), 2, 8, 10);
       
   393     }
       
   394 
       
   395     @Test
       
   396     public void testCrLfEof2() throws Exception {
       
   397         assertPosition(findSection("abc\r\nxyz\r\n"), 2, 9, 10);
       
   398     }
       
   399 
       
   400     @Test
       
   401     public void testMainSectionNotTerminatedWithLineBreak() throws Exception {
       
   402         assertNull(findSection("abc\r\nxyz\r\n   "));
       
   403     }
       
   404 
       
   405     @Test
       
   406     public void testLfCrEof() throws Exception {
       
   407         catchCrCausesIndexOutOfBoundsException(
       
   408                 () -> findSection("abc\r\nxyz\n\r"),
       
   409                 p -> assertPosition(p,
       
   410                         2, actualEndOfSection8217375(8, true, 1), 10)
       
   411         );
       
   412     }
       
   413 
       
   414     @Test
       
   415     public void testLfCrContinued() throws Exception {
       
   416         assertPosition(findSection("abc\r\nxyz\n\r   "), 2, 8, 10);
       
   417     }
       
   418 
       
   419     @Test
       
   420     public void testCrLfCrEof() throws Exception {
       
   421         catchCrCausesIndexOutOfBoundsException(
       
   422                 () -> findSection("abc\r\nxyz\r\n\r"),
       
   423                 p -> assertPosition(p,
       
   424                         2, actualEndOfSection8217375(9, true, 2), 11)
       
   425         );
       
   426     }
       
   427 
       
   428     @Test
       
   429     public void testCrLfCrContinued() throws Exception {
       
   430         assertPosition(findSection("abc\r\nxyz\r\n\r   "), 2, 9, 11);
       
   431     }
       
   432 
       
   433     @Test
       
   434     public void testCrLfLfEof() throws Exception {
       
   435         assertPosition(findSection("abc\r\nxyz\r\n\n"),
       
   436                 2, actualEndOfSection8217375(9, true, 1), 11);
       
   437     }
       
   438 
       
   439     @Test
       
   440     public void testCrLfLfContinued() throws Exception {
       
   441         assertPosition(findSection("abc\r\nxyz\r\n\n   "), 2, 9, 11);
       
   442     }
       
   443 
       
   444     @Test
       
   445     public void testCrLfCrLfEof() throws Exception {
       
   446         assertPosition(findSection("abc\r\nxyz\r\n\r\n"),
       
   447                 2, actualEndOfSection8217375(9, true, 2), 12);
       
   448     }
       
   449 
       
   450     @Test
       
   451     public void testCrLfCfLfContinued() throws Exception {
       
   452         assertPosition(findSection("abc\r\nxyz\r\n\r\n   "), 2, 9, 12);
       
   453     }
       
   454 
       
   455     @Test
       
   456     public void testCrLfCrCrEof() throws Exception {
       
   457         assertPosition(findSection("abc\r\nxyz\r\n\r\r"), 2, 9, 11);
       
   458     }
       
   459 
       
   460     @Test
       
   461     public void testCrLfCrCrContinued() throws Exception {
       
   462         assertPosition(findSection("abc\r\nxyz\r\n\r\r   "), 2, 9, 11);
       
   463     }
       
   464 
       
   465     @Test
       
   466     public void testCrLfLfCrEof() throws Exception {
       
   467         assertPosition(findSection("abc\r\nxyz\r\n\n\r"), 2, 9, 11);
       
   468     }
       
   469 
       
   470     @Test
       
   471     public void testCrLfLfCrContinued() throws Exception {
       
   472         assertPosition(findSection("abc\r\nxyz\r\n\n\r   "), 2, 9, 11);
       
   473     }
       
   474 
       
   475     @Test
       
   476     public void testCrLfCrLfCrEof() throws Exception {
       
   477         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r"), 2, 9, 12);
       
   478     }
       
   479 
       
   480     @Test
       
   481     public void testCrLfCfLfCrContinued() throws Exception {
       
   482         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r   "), 2, 9, 12);
       
   483     }
       
   484 
       
   485     @Test
       
   486     public void testCrLfCrLfContinued() throws Exception {
       
   487         assertPosition(findSection("abc\r\nxyz\r\n\r\n   "), 2, 9, 12);
       
   488     }
       
   489 
       
   490     @Test
       
   491     public void testCrLfLfLfEof() throws Exception {
       
   492         assertPosition(findSection("abc\r\nxyz\r\n\n\n"), 2, 9, 11);
       
   493     }
       
   494 
       
   495     @Test
       
   496     public void testCrLfLfLfContinued() throws Exception {
       
   497         assertPosition(findSection("abc\r\nxyz\r\n\n\n   "), 2, 9, 11);
       
   498     }
       
   499 
       
   500     @Test
       
   501     public void testCrLfCrLfLfContinued() throws Exception {
       
   502         assertPosition(findSection("abc\r\nxyz\r\n\r\n\n   "), 2, 9, 12);
       
   503     }
       
   504 
       
   505     @Test
       
   506     public void testCrLfCrCrLfEof() throws Exception {
       
   507         assertPosition(findSection("abc\r\nxyz\r\n\r\r\n"), 2, 9, 11);
       
   508     }
       
   509 
       
   510     @Test
       
   511     public void testCrLfCrCrLfContinued() throws Exception {
       
   512         assertPosition(findSection("abc\r\nxyz\r\n\r\r\n   "), 2, 9, 11);
       
   513     }
       
   514 
       
   515     @Test
       
   516     public void testCrLfLfCrLfEof() throws Exception {
       
   517         assertPosition(findSection("abc\r\nxyz\r\n\n\r\n"), 2, 9, 11);
       
   518     }
       
   519 
       
   520     @Test
       
   521     public void testCrLfLfCrLfContinued() throws Exception {
       
   522         assertPosition(findSection("abc\r\nxyz\r\n\n\r\n   "), 2, 9, 11);
       
   523     }
       
   524 
       
   525     @Test
       
   526     public void testCrLfCrLfCrLfEof() throws Exception {
       
   527         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n"), 2, 9, 12);
       
   528     }
       
   529 
       
   530     @Test
       
   531     public void testCrLfCfLfCrLfContinued() throws Exception {
       
   532         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n   "), 2, 9, 12);
       
   533     }
       
   534 
       
   535     @Test
       
   536     public void testCrLfLfCrCrEof() throws Exception {
       
   537         assertPosition(findSection("abc\r\nxyz\r\n\n\r\r"), 2, 9, 11);
       
   538     }
       
   539 
       
   540     @Test
       
   541     public void testCrLfCrLfCrCrEof() throws Exception {
       
   542         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r"), 2, 9, 12);
       
   543     }
       
   544 
       
   545     @Test
       
   546     public void testCrLfCrLfCrContinued() throws Exception {
       
   547         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r   "), 2, 9, 12);
       
   548     }
       
   549 
       
   550     @Test
       
   551     public void testCrLfLfLfCrEof() throws Exception {
       
   552         assertPosition(findSection("abc\r\nxyz\r\n\n\n\r"), 2, 9, 11);
       
   553     }
       
   554 
       
   555     @Test
       
   556     public void testCrLfLfCrLfCrEof() throws Exception {
       
   557         assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r"), 2, 9, 11);
       
   558     }
       
   559 
       
   560     @Test
       
   561     public void testCrLfLfLfLfEof() throws Exception {
       
   562         assertPosition(findSection("abc\r\nxyz\r\n\n\n\n"), 2, 9, 11);
       
   563     }
       
   564 
       
   565     @Test
       
   566     public void testCrLfLfCrLfLfEof() throws Exception {
       
   567         assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\n"), 2, 9, 11);
       
   568     }
       
   569 
       
   570     @Test
       
   571     public void testCrLfLfCrCrLfEof() throws Exception {
       
   572         assertPosition(findSection("abc\r\nxyz\r\n\n\r\r\n"), 2, 9, 11);
       
   573     }
       
   574 
       
   575     @Test
       
   576     public void testCrLfCrLfCrCrLfEof() throws Exception {
       
   577         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r\n"), 2, 9, 12);
       
   578     }
       
   579 
       
   580     @Test
       
   581     public void testCrLfCrLfCrLfContinued() throws Exception {
       
   582         assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n   "), 2, 9, 12);
       
   583     }
       
   584 
       
   585     @Test
       
   586     public void testCrLfLfLfCrLfEof() throws Exception {
       
   587         assertPosition(findSection("abc\r\nxyz\r\n\n\n\r\n"), 2, 9, 11);
       
   588     }
       
   589 
       
   590     @Test
       
   591     public void testCrLfLfCrLfCrLfEof() throws Exception {
       
   592         assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r\n"), 2, 9, 11);
       
   593     }
       
   594 
       
   595     @Test
       
   596     public void testCrLfCrCrLfCrCrEof() throws Exception {
       
   597         assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r"), 2, 9, 11);
       
   598     }
       
   599 
       
   600     @Test
       
   601     public void testCrLfCrCrCrCrEof() throws Exception {
       
   602         assertPosition(findSection("abc\r\nxyz\r\n\r\r\r"), 2, 9, 11);
       
   603     }
       
   604 
       
   605     @Test
       
   606     public void testCrLfCrCrLfLfEof() throws Exception {
       
   607         assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\n"), 2, 9, 11);
       
   608     }
       
   609 
       
   610     @Test
       
   611     public void testCrLfCrCrLfCrLfEof() throws Exception {
       
   612         assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r\n"), 2, 9, 11);
       
   613     }
       
   614 
       
   615     @Test
       
   616     public void testCrLfCrCrCrLfEof() throws Exception {
       
   617         assertPosition(findSection("abc\r\nxyz\r\n\r\r\r\n"), 2, 9, 11);
       
   618     }
       
   619 
       
   620     /*
       
   621      * endOfFirstLine is the same regardless of the line break delimiter
       
   622      */
       
   623     @Test
       
   624     public void testEndOfFirstLineVsLineBreak() throws Exception {
       
   625         for (String lb : new String[] { "\r", "\n", "\r\n" }) {
       
   626             Position p = findSection("abc" + lb + "xyz" + lb + lb + " ");
       
   627 
       
   628             // main assertion showing endOfFirstLine independent of line break
       
   629             assertEquals(p.endOfFirstLine, 2 + offset);
       
   630 
       
   631             // assert remaining positions as well just for completeness
       
   632             assertPosition(p, 2, 5 + 2 * lb.length(), 6 + 3 * lb.length());
       
   633         }
       
   634     }
       
   635 
       
   636     /*
       
   637      * '\r' at the end of the bytes causes index out of bounds exception
       
   638      */
       
   639     @Test
       
   640     public void testCrLastCausesIndexOutOfBounds() throws Exception {
       
   641         catchCrCausesIndexOutOfBoundsException(
       
   642                 () -> findSection("\r"),
       
   643                 p -> assertPosition(p,
       
   644                         -1, actualEndOfSection8217375(-1, true, 1), 1)
       
   645         );
       
   646     }
       
   647 
       
   648     /*
       
   649      * endOfSection includes second line break if at end of bytes only
       
   650      */
       
   651     @Test
       
   652     public void testEndOfSectionWithLineBreakVsEof() throws Exception {
       
   653         AssertionError errors = new AssertionError("offset = " + offset);
       
   654         for (String lb : new String[] { "\r", "\n", "\r\n" }) {
       
   655             for (boolean eof : new boolean[] { false, true }) {
       
   656                 Position p;
       
   657                 try {
       
   658                     p = findSection("abc" + lb + lb + (eof ? "" : "xyz"));
       
   659                 } catch (RuntimeException | ReflectiveOperationException e) {
       
   660                     if ((e instanceof IndexOutOfBoundsException ||
       
   661                          e.getCause() instanceof IndexOutOfBoundsException)
       
   662                           && eof && "\r".equals(lb) && !FIXED_8217375) continue;
       
   663                     throw e;
       
   664                 }
       
   665 
       
   666                 AssertionError a = new AssertionError("offset = " + offset
       
   667                         + ", lb = " + Utils.escapeStringWithNumbers(lb) + ", "
       
   668                         + "eof = " + eof);
       
   669 
       
   670                 // main assertion showing endOfSection including second line
       
   671                 // break when at end of file
       
   672                 a = collectErrors(a, () -> assertEquals(
       
   673                         p.endOfSection,
       
   674                         actualEndOfSection8217375(
       
   675                                 2 + lb.length() + offset, eof, lb.length()) ));
       
   676 
       
   677                 // assert remaining positions as well just for completeness
       
   678                 a = collectErrors(a, () -> assertPosition(p,
       
   679                         2,
       
   680                         actualEndOfSection8217375(
       
   681                                 2 + lb.length(), eof, lb.length()),
       
   682                         3 + lb.length() * 2));
       
   683 
       
   684                 if (a.getSuppressed().length > 0) errors.addSuppressed(a);
       
   685             }
       
   686         }
       
   687         if (errors.getSuppressed().length > 0) throw errors;
       
   688     }
       
   689 
       
   690     /*
       
   691      * returns position even if only one line break before end of bytes.
       
   692      * because no name will be found the result will be skipped and no entry
       
   693      * will be created.
       
   694      */
       
   695     @Test
       
   696     public void testReturnPosVsEof() throws Exception {
       
   697         for (String lb : new String[] { "\r", "\n", "\r\n" }) {
       
   698             for (boolean eof : new boolean[] { false, true }) {
       
   699                 try {
       
   700                     Position p = findSection("abc" + lb + (eof ? "" : "xyz"));
       
   701                     assertTrue(p != null == eof);
       
   702                 } catch (RuntimeException | ReflectiveOperationException e) {
       
   703                     if ((e instanceof IndexOutOfBoundsException ||
       
   704                          e.getCause() instanceof IndexOutOfBoundsException)
       
   705                           && eof && "\r".equals(lb) && !FIXED_8217375) continue;
       
   706                     throw e;
       
   707                 }
       
   708             }
       
   709         }
       
   710     }
       
   711 
       
   712     /*
       
   713      * it could be normally be expected that startOfNext would point to the
       
   714      * start of the next section after a blank line but that is not the case
       
   715      * if a section ends with only one line break and no blank line immediately
       
   716      * before eof of the manifest.
       
   717      * such an entry will be digested without the trailing blank line which is
       
   718      * only fine until another section should be added afterwards.
       
   719      */
       
   720     @Test
       
   721     public void testStartOfNextPointsToEofWithNoBlankLine() throws Exception {
       
   722         for (String lb : new String[] { "\r", "\n", "\r\n" }) {
       
   723             for (boolean blank : new boolean[] { false, true }) {
       
   724                 String manifest = "abc" + lb + "xyz" + lb + (blank ? lb : "");
       
   725                 try {
       
   726                     Position p = findSection(manifest);
       
   727 
       
   728                     // assert that startOfNext points to eof in all cases
       
   729                     // whether with or without a blank line before eof
       
   730                     assertEquals(p.startOfNext, manifest.length() + offset);
       
   731 
       
   732                     // assert remaining positions as well just for completeness
       
   733                     assertPosition(p,
       
   734                             2,
       
   735                             actualEndOfSection8217375(
       
   736                                     5 + lb.length() * 2,
       
   737                                     true,
       
   738                                     blank ? lb.length() : 0),
       
   739                             manifest.length());
       
   740                 } catch (RuntimeException | ReflectiveOperationException e) {
       
   741                     if ((e instanceof IndexOutOfBoundsException ||
       
   742                          e.getCause() instanceof IndexOutOfBoundsException)
       
   743                           && "\r".equals(lb) && !FIXED_8217375) continue;
       
   744                     throw e;
       
   745                 }
       
   746             }
       
   747         }
       
   748     }
       
   749 
       
   750 }