| /* | |
| * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| import java.lang.reflect.Constructor; | |
| import java.lang.reflect.Field; | |
| import java.lang.reflect.Method; | |
| import java.util.Arrays; | |
| import java.util.concurrent.Callable; | |
| import java.util.function.Consumer; | |
| import sun.security.util.ManifestDigester; | |
| import org.testng.annotations.Test; | |
| import org.testng.annotations.BeforeClass; | |
| import org.testng.annotations.BeforeMethod; | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Factory; | |
| import static java.nio.charset.StandardCharsets.UTF_8; | |
| import static org.testng.Assert.*; | |
| /** | |
| * @test | |
| * @bug 8217375 | |
| * @modules java.base/sun.security.util:+open | |
| * @compile ../../tools/jarsigner/Utils.java | |
| * @run testng/othervm FindSection | |
| * @summary Check {@link ManifestDigester#findSection}. | |
| */ | |
| public class FindSection { | |
| /* | |
| * TODO: | |
| * FIXED_8217375 is not intended to keep. it is intended to show what | |
| * exactly has changed with respect to the previous version for which no | |
| * such test existed. | |
| */ | |
| static final boolean FIXED_8217375 = true; | |
| /** | |
| * {@link ManifestDigester.Entry#digestWorkaround} should not feed the | |
| * trailing blank line into the digester. Before resolution of 8217375 it | |
| * fed the trailing blank line into the digest if the second line break | |
| * was at the end of the file due to <pre> | |
| * if (allBlank || (i == len-1)) { | |
| * if (i == len-1) | |
| * pos.endOfSection = i; | |
| * else | |
| * pos.endOfSection = last; | |
| * </pre> in {@link ManifestDigester#findSection}. In that case at the end | |
| * of the manifest file, {@link ManifestDigester.Entry#digestWorkaround} | |
| * would have produced the same digest as | |
| * {@link ManifestDigester.Entry#digest} which was wrong and without effect | |
| * at best. | |
| * <p> | |
| * Once this fix is accepted, this flag can be removed along with | |
| * {@link #actualEndOfSection8217375}. | |
| */ | |
| static final boolean FIXED_8217375_EOF_ENDOFSECTION = FIXED_8217375; | |
| /** | |
| * {@link ManifestDigester.Position.endOfSection} usually points to the | |
| * start position of the blank line trailing a section minus one. | |
| * If a {@link ManifestDigester.Position} returned by | |
| * {@link ManifestDigester#findSection} is based on a portion that starts | |
| * with a blank line, above statement is (or was) not true, because of the | |
| * initialization of {@code last} in {@link ManifestDigester#findSection} | |
| * <pre> | |
| * int last = offset; | |
| * </pre> | |
| * which would point after incrementing it in {@code pos.endOfSection + 1} | |
| * on line 128 (line number before this change) or {@code int sectionLen = | |
| * pos.endOfSection-start+1;} on line 133 (line number before this change) | |
| * at one byte after the first line break character of usually two and | |
| * possibly (assuming "{@code \r\n}" default line break normally) in between | |
| * the two characters of a line break. After subtracting again the index of | |
| * the section start position on former line 133, the last byte would be | |
| * missed to be digested by {@link ManifestDigester.Entry#digestWorkaround}. | |
| * <p> | |
| * All this, however could possibly matter (or have mattered) only when | |
| * {@link ManifestDigester#findSection} was invoked with an offset position | |
| * pointing straight to a line break which happens if a manifest starts | |
| * with an empty line or if there are superfluous blank lines between | |
| * sections in both cases no useful manifest portion is identified. | |
| * Superfluous blank lines are not identified as sections (because they | |
| * don't have a name and specifically don't meet {@code if (len > 6) {} on | |
| * former line 136. Manifests starting with a line break are not any more | |
| * useful either. | |
| * <p> | |
| * Once this fix is accepted, this flag can be removed along with | |
| * {@link #actualEndOfSection8217375}. | |
| */ | |
| static final boolean FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION = | |
| FIXED_8217375; | |
| static Constructor<?> PositionConstructor; | |
| static Method findSection; | |
| static Field rawBytes; | |
| static Field endOfFirstLine; | |
| static Field endOfSection; | |
| static Field startOfNext; | |
| @BeforeClass | |
| public static void setFindSectionAccessible() throws Exception { | |
| Class<?> Position = Arrays.stream(ManifestDigester.class. | |
| getDeclaredClasses()).filter(c -> c.getSimpleName(). | |
| equals("Position")).findFirst().get(); | |
| PositionConstructor = Position.getDeclaredConstructor(); | |
| PositionConstructor.setAccessible(true); | |
| findSection = ManifestDigester.class.getDeclaredMethod("findSection", | |
| int.class, Position); | |
| findSection.setAccessible(true); | |
| rawBytes = ManifestDigester.class.getDeclaredField("rawBytes"); | |
| rawBytes.setAccessible(true); | |
| endOfFirstLine = Position.getDeclaredField("endOfFirstLine"); | |
| endOfFirstLine.setAccessible(true); | |
| endOfSection = Position.getDeclaredField("endOfSection"); | |
| endOfSection.setAccessible(true); | |
| startOfNext = Position.getDeclaredField("startOfNext"); | |
| startOfNext.setAccessible(true); | |
| } | |
| static class Position { | |
| final int endOfFirstLine; // not including newline character | |
| final int endOfSection; // end of section, not including the blank line | |
| // between sections | |
| final int startOfNext; // the start of the next section | |
| Position(Object pos) throws ReflectiveOperationException { | |
| endOfFirstLine = FindSection.endOfFirstLine.getInt(pos); | |
| endOfSection = FindSection.endOfSection.getInt(pos); | |
| startOfNext = FindSection.startOfNext.getInt(pos); | |
| } | |
| } | |
| Position findSection(byte[] manifestBytes) | |
| throws ReflectiveOperationException { | |
| ManifestDigester manDig = new ManifestDigester("\n\n".getBytes(UTF_8)); | |
| FindSection.rawBytes.set(manDig, manifestBytes); | |
| Object pos = PositionConstructor.newInstance(); | |
| Object result = findSection.invoke(manDig, offset, pos); | |
| if (Boolean.FALSE.equals(result)) { | |
| return null; // indicates findSection having returned false | |
| } else { | |
| return new Position(pos); | |
| } | |
| } | |
| @DataProvider(name = "parameters") | |
| public static Object[][] parameters() { | |
| return new Object[][] { { 0 }, { 42 } }; | |
| } | |
| @Factory(dataProvider = "parameters") | |
| public static Object[] createTests(int offset) { | |
| return new Object[]{ new FindSection(offset) }; | |
| } | |
| final int offset; | |
| FindSection(int offset) { | |
| this.offset = offset; | |
| } | |
| @BeforeMethod | |
| public void verbose() { | |
| System.out.println("offset = " + offset); | |
| } | |
| Position findSection(String manifestString) | |
| throws ReflectiveOperationException { | |
| byte[] manifestBytes = manifestString.getBytes(UTF_8); | |
| byte[] manifestWithOffset = new byte[manifestBytes.length + offset]; | |
| System.arraycopy(manifestBytes, 0, manifestWithOffset, offset, | |
| manifestBytes.length); | |
| return findSection(manifestWithOffset); | |
| } | |
| /** | |
| * Surprising, but the offset actually makes a difference in | |
| * {@link ManifestDigester#findSection} return value. | |
| */ | |
| @SuppressWarnings("unused") | |
| int actualEndOfFirstLine8217375(int correctPosition) { | |
| // if the parsed portion of the manifest starts with a blank line, | |
| // and offset is 0, "pos.endOfFirstLine = -1;" probably denoting a | |
| // yet uninitialized value coincides with the assignment by | |
| // "pos.endOfFirstLine = i-1;" if i == 0 and | |
| // "if (pos.endOfFirstLine == -1)" after "case '\n':" happens to | |
| // become true even though already assigned. | |
| if (offset == 0 && correctPosition == -1 && !FIXED_8217375) return 0; | |
| return correctPosition; | |
| } | |
| @SuppressWarnings("unused") | |
| int actualEndOfSection8217375(int correctPosition, boolean eof, int lbl) { | |
| // if the parsed portion of the manifest ends with a blank line and | |
| // just before eof, the blank line is included in Position.endOfSection/ | |
| // Section.length (the one usually without blank line as well as in | |
| // Position.startOfNext/Section.lengthWithBlankLine) which is used | |
| // in digestWorkaround (independent of the digest without workaround) | |
| if (eof && !FIXED_8217375_EOF_ENDOFSECTION) { | |
| return correctPosition + lbl; | |
| } else if (correctPosition == -1 | |
| && !FIXED_8217375_STARTWITHBLANKLINE_ENDOFSECTION) { | |
| return 0; | |
| } else { | |
| return correctPosition; | |
| } | |
| } | |
| AssertionError collectErrors(AssertionError a, Runnable run) { | |
| try { | |
| run.run(); | |
| } catch (AssertionError e) { | |
| if (a == null) a = new AssertionError(); | |
| a.addSuppressed(e); | |
| } | |
| return a; | |
| } | |
| void assertPosition(Position pos, | |
| int endOfFirstLine, int endOfSection, int startOfNext) { | |
| AssertionError a = null; | |
| a = collectErrors(a, () -> assertEquals( | |
| pos.endOfFirstLine, endOfFirstLine + offset, "endOfFirstLine")); | |
| a = collectErrors(a, () -> assertEquals( | |
| pos.endOfSection, endOfSection + offset, "endOfSection")); | |
| a = collectErrors(a, () -> assertEquals( | |
| pos.startOfNext, startOfNext + offset, "startOfNext")); | |
| if (a != null) throw a; | |
| } | |
| void catchCrCausesIndexOutOfBoundsException( | |
| Callable<Position> test, Consumer<Position> asserts) { | |
| try { | |
| Position x = test.call(); | |
| if (!FIXED_8217375) fail(); | |
| asserts.accept(x); | |
| } catch (Exception e) { | |
| if (e instanceof IndexOutOfBoundsException || | |
| e.getCause() instanceof IndexOutOfBoundsException) { | |
| if (FIXED_8217375) throw new AssertionError(e); | |
| } else { | |
| throw new AssertionError(e); | |
| } | |
| } | |
| } | |
| @Test | |
| public void testEmpty() throws Exception { | |
| assertNull(findSection("")); | |
| } | |
| @Test | |
| public void testOneLineBreakCr() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("\r"), | |
| p -> assertPosition(p, | |
| -1, actualEndOfSection8217375(-1, false, 1), 1) | |
| ); | |
| } | |
| @Test | |
| public void testOneLineBreakLf() throws Exception { | |
| assertPosition(findSection("\n"), | |
| -1, actualEndOfSection8217375(-1, false, 1), 1); | |
| } | |
| @Test | |
| public void testOneLineBreakCrLf() throws Exception { | |
| assertPosition(findSection("\r\n"), | |
| actualEndOfFirstLine8217375(-1), | |
| actualEndOfSection8217375(-1, true, 2), | |
| 2); | |
| } | |
| @Test | |
| public void testSpaceAndLineBreakCr() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection(" \r"), | |
| p -> assertPosition(p, 2, 3, 4) | |
| ); | |
| } | |
| @Test | |
| public void testSpaceAndOneLineBreakLf() throws Exception { | |
| assertPosition(findSection(" \n"), 2, 3, 4); | |
| } | |
| @Test | |
| public void testSpaceAndOneLineBreakCrLf() throws Exception { | |
| assertPosition(findSection(" \r\n"), 2, 4, 5); | |
| } | |
| @Test | |
| public void testOneLineBreakCrAndSpace() throws Exception { | |
| assertPosition(findSection("\r "), | |
| -1, actualEndOfSection8217375(-1, false, 1), 1); | |
| } | |
| @Test | |
| public void testOneLineBreakLfAndSpace() throws Exception { | |
| assertPosition(findSection("\n "), | |
| -1, actualEndOfSection8217375(-1, false, 1), 1); | |
| } | |
| @Test | |
| public void testOneLineBreakCrLfAndSpace() throws Exception { | |
| assertPosition(findSection("\r\n "), | |
| actualEndOfFirstLine8217375(-1), | |
| actualEndOfSection8217375(-1, false, 1), | |
| 2); | |
| } | |
| @Test | |
| public void testCrEof() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("abc\r"), | |
| p -> assertPosition(p, 2, 3, 4) | |
| ); | |
| } | |
| @Test | |
| public void testLfEof() throws Exception { | |
| assertPosition(findSection("abc\n"), 2, 3, 4); | |
| } | |
| @Test | |
| public void testCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\n"), 2, 4, 5); | |
| } | |
| @Test | |
| public void testCrContinued() throws Exception { | |
| assertPosition(findSection("abc\rxyz\r\n\r\n "), 2, 8, 11); | |
| } | |
| @Test | |
| public void testLfContinued() throws Exception { | |
| assertPosition(findSection("abc\nxyz\r\n\r\n "), 2, 8, 11); | |
| } | |
| @Test | |
| public void testCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrCrEof() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("abc\r\nxyz\r\r"), | |
| p -> assertPosition(p, | |
| 2, actualEndOfSection8217375(8, true, 1), 10) | |
| ); | |
| } | |
| @Test | |
| public void testCrCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\r "), 2, 8, 10); | |
| } | |
| @Test | |
| public void testLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\n\n"), | |
| 2, actualEndOfSection8217375(8, true, 1), 10); | |
| } | |
| @Test | |
| public void testLfLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\n\n "), 2, 8, 10); | |
| } | |
| @Test | |
| public void testCrLfEof2() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n"), 2, 9, 10); | |
| } | |
| @Test | |
| public void testMainSectionNotTerminatedWithLineBreak() throws Exception { | |
| assertNull(findSection("abc\r\nxyz\r\n ")); | |
| } | |
| @Test | |
| public void testLfCrEof() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("abc\r\nxyz\n\r"), | |
| p -> assertPosition(p, | |
| 2, actualEndOfSection8217375(8, true, 1), 10) | |
| ); | |
| } | |
| @Test | |
| public void testLfCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\n\r "), 2, 8, 10); | |
| } | |
| @Test | |
| public void testCrLfCrEof() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("abc\r\nxyz\r\n\r"), | |
| p -> assertPosition(p, | |
| 2, actualEndOfSection8217375(9, true, 2), 11) | |
| ); | |
| } | |
| @Test | |
| public void testCrLfCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n"), | |
| 2, actualEndOfSection8217375(9, true, 1), 11); | |
| } | |
| @Test | |
| public void testCrLfLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n"), | |
| 2, actualEndOfSection8217375(9, true, 2), 12); | |
| } | |
| @Test | |
| public void testCrLfCfLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCrCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r"), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCfLfCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\n "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCrCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\n "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\n "), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n"), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCfLfCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfLfCrCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r"), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfLfLfCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\n\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrLfCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\n\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\r\n"), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfCrLfCrLfContinued() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\n\r\n "), 2, 9, 12); | |
| } | |
| @Test | |
| public void testCrLfLfLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\n\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfLfCrLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\n\r\n\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrLfCrCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrCrCrEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\r"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrLfLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrLfCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\n\r\n"), 2, 9, 11); | |
| } | |
| @Test | |
| public void testCrLfCrCrCrLfEof() throws Exception { | |
| assertPosition(findSection("abc\r\nxyz\r\n\r\r\r\n"), 2, 9, 11); | |
| } | |
| /* | |
| * endOfFirstLine is the same regardless of the line break delimiter | |
| */ | |
| @Test | |
| public void testEndOfFirstLineVsLineBreak() throws Exception { | |
| for (String lb : new String[] { "\r", "\n", "\r\n" }) { | |
| Position p = findSection("abc" + lb + "xyz" + lb + lb + " "); | |
| // main assertion showing endOfFirstLine independent of line break | |
| assertEquals(p.endOfFirstLine, 2 + offset); | |
| // assert remaining positions as well just for completeness | |
| assertPosition(p, 2, 5 + 2 * lb.length(), 6 + 3 * lb.length()); | |
| } | |
| } | |
| /* | |
| * '\r' at the end of the bytes causes index out of bounds exception | |
| */ | |
| @Test | |
| public void testCrLastCausesIndexOutOfBounds() throws Exception { | |
| catchCrCausesIndexOutOfBoundsException( | |
| () -> findSection("\r"), | |
| p -> assertPosition(p, | |
| -1, actualEndOfSection8217375(-1, true, 1), 1) | |
| ); | |
| } | |
| /* | |
| * endOfSection includes second line break if at end of bytes only | |
| */ | |
| @Test | |
| public void testEndOfSectionWithLineBreakVsEof() throws Exception { | |
| AssertionError errors = new AssertionError("offset = " + offset); | |
| for (String lb : new String[] { "\r", "\n", "\r\n" }) { | |
| for (boolean eof : new boolean[] { false, true }) { | |
| Position p; | |
| try { | |
| p = findSection("abc" + lb + lb + (eof ? "" : "xyz")); | |
| } catch (RuntimeException | ReflectiveOperationException e) { | |
| if ((e instanceof IndexOutOfBoundsException || | |
| e.getCause() instanceof IndexOutOfBoundsException) | |
| && eof && "\r".equals(lb) && !FIXED_8217375) continue; | |
| throw e; | |
| } | |
| AssertionError a = new AssertionError("offset = " + offset | |
| + ", lb = " + Utils.escapeStringWithNumbers(lb) + ", " | |
| + "eof = " + eof); | |
| // main assertion showing endOfSection including second line | |
| // break when at end of file | |
| a = collectErrors(a, () -> assertEquals( | |
| p.endOfSection, | |
| actualEndOfSection8217375( | |
| 2 + lb.length() + offset, eof, lb.length()) )); | |
| // assert remaining positions as well just for completeness | |
| a = collectErrors(a, () -> assertPosition(p, | |
| 2, | |
| actualEndOfSection8217375( | |
| 2 + lb.length(), eof, lb.length()), | |
| 3 + lb.length() * 2)); | |
| if (a.getSuppressed().length > 0) errors.addSuppressed(a); | |
| } | |
| } | |
| if (errors.getSuppressed().length > 0) throw errors; | |
| } | |
| /* | |
| * returns position even if only one line break before end of bytes. | |
| * because no name will be found the result will be skipped and no entry | |
| * will be created. | |
| */ | |
| @Test | |
| public void testReturnPosVsEof() throws Exception { | |
| for (String lb : new String[] { "\r", "\n", "\r\n" }) { | |
| for (boolean eof : new boolean[] { false, true }) { | |
| try { | |
| Position p = findSection("abc" + lb + (eof ? "" : "xyz")); | |
| assertTrue(p != null == eof); | |
| } catch (RuntimeException | ReflectiveOperationException e) { | |
| if ((e instanceof IndexOutOfBoundsException || | |
| e.getCause() instanceof IndexOutOfBoundsException) | |
| && eof && "\r".equals(lb) && !FIXED_8217375) continue; | |
| throw e; | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| * it could be normally be expected that startOfNext would point to the | |
| * start of the next section after a blank line but that is not the case | |
| * if a section ends with only one line break and no blank line immediately | |
| * before eof of the manifest. | |
| * such an entry will be digested without the trailing blank line which is | |
| * only fine until another section should be added afterwards. | |
| */ | |
| @Test | |
| public void testStartOfNextPointsToEofWithNoBlankLine() throws Exception { | |
| for (String lb : new String[] { "\r", "\n", "\r\n" }) { | |
| for (boolean blank : new boolean[] { false, true }) { | |
| String manifest = "abc" + lb + "xyz" + lb + (blank ? lb : ""); | |
| try { | |
| Position p = findSection(manifest); | |
| // assert that startOfNext points to eof in all cases | |
| // whether with or without a blank line before eof | |
| assertEquals(p.startOfNext, manifest.length() + offset); | |
| // assert remaining positions as well just for completeness | |
| assertPosition(p, | |
| 2, | |
| actualEndOfSection8217375( | |
| 5 + lb.length() * 2, | |
| true, | |
| blank ? lb.length() : 0), | |
| manifest.length()); | |
| } catch (RuntimeException | ReflectiveOperationException e) { | |
| if ((e instanceof IndexOutOfBoundsException || | |
| e.getCause() instanceof IndexOutOfBoundsException) | |
| && "\r".equals(lb) && !FIXED_8217375) continue; | |
| throw e; | |
| } | |
| } | |
| } | |
| } | |
| } |