| /* | |
| * Copyright (c) 2018, 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 static java.nio.charset.StandardCharsets.UTF_8; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.IOException; | |
| import java.util.jar.Attributes; | |
| import java.util.jar.Manifest; | |
| import java.util.jar.Attributes.Name; | |
| import java.lang.reflect.Field; | |
| import org.testng.annotations.Test; | |
| import static org.testng.Assert.*; | |
| /** | |
| * @test | |
| * @bug 8066619 | |
| * @modules java.base/java.util.jar:+open | |
| * @run testng/othervm NullAndEmptyKeysAndValues | |
| * @summary Tests manifests with {@code null} and empty string {@code ""} | |
| * values as section name, header name, or value in both main and named | |
| * attributes sections. | |
| */ | |
| /* | |
| * Note to future maintainer: | |
| * In order to actually being able to test all the cases where key and values | |
| * are null normal manifest and attributes manipulation through their public | |
| * api is not sufficient but then there were these null checks there before | |
| * which may or may not have had their reason and this way it's ensured that | |
| * the behavior does not change with that respect. | |
| * Once module isolation is enforced some test cases will not any longer be | |
| * possible and those now tested situations will be guaranteed not to occur | |
| * any longer at all at which point the corresponding tests can be removed | |
| * safely without replacement unless of course another way is found inject the | |
| * tested null values. | |
| * Another trick to access package private class members could be to use | |
| * deserialization or adding a new class to the same package on the classpath. | |
| * Here is not important how the values are set to null because it shows that | |
| * the behavior remains unchanged. | |
| */ | |
| public class NullAndEmptyKeysAndValues { | |
| static final String SOME_KEY = "some-key"; | |
| static final String SOME_VALUE = "some value"; | |
| static final String NULL_TEXT = "null"; | |
| static final String EMPTY_STR = ""; | |
| static final Name EMPTY_NAME = new Name("tmp") {{ | |
| try { | |
| Field name = Name.class.getDeclaredField("name"); | |
| name.setAccessible(true); | |
| name.set(this, EMPTY_STR); | |
| } catch (Exception e) { | |
| throw new RuntimeException(e.getMessage(), e); | |
| } | |
| }}; | |
| @Test | |
| public void testMainAttributesHeaderNameNull() throws Exception { | |
| Manifest mf = new Manifest(); | |
| Field attr = mf.getClass().getDeclaredField("attr"); | |
| attr.setAccessible(true); | |
| Attributes mainAtts = new Attributes() {{ | |
| super.put(null, SOME_VALUE); | |
| }}; | |
| attr.set(mf, mainAtts); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| assertThrows(NullPointerException.class, () -> writeAndRead(mf)); | |
| } | |
| @Test | |
| public void testMainAttributesHeaderNameEmpty() throws Exception { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getMainAttributes().put(EMPTY_NAME, SOME_VALUE); | |
| assertThrows(IOException.class, () -> writeAndRead(mf)); | |
| } | |
| @Test | |
| public void testMainAttributesHeaderValueNull() throws Exception { | |
| Manifest mf = new Manifest(); | |
| Field attr = mf.getClass().getDeclaredField("attr"); | |
| attr.setAccessible(true); | |
| Attributes mainAtts = new Attributes() {{ | |
| map.put(new Name(SOME_KEY), null); | |
| }}; | |
| attr.set(mf, mainAtts); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf = writeAndRead(mf); | |
| assertEquals(mf.getMainAttributes().getValue(SOME_KEY), NULL_TEXT); | |
| } | |
| @Test | |
| public void testMainAttributesHeaderValueEmpty() throws Exception { | |
| Manifest mf = new Manifest(); | |
| Field attr = mf.getClass().getDeclaredField("attr"); | |
| attr.setAccessible(true); | |
| Attributes mainAtts = new Attributes() {{ | |
| map.put(new Name(SOME_KEY), EMPTY_STR); | |
| }}; | |
| attr.set(mf, mainAtts); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf = writeAndRead(mf); | |
| assertEquals(mf.getMainAttributes().getValue(SOME_KEY), EMPTY_STR); | |
| } | |
| @Test | |
| public void testSectionNameNull() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(null, new Attributes()); | |
| mf = writeAndRead(mf); | |
| assertNotNull(mf.getEntries().get(NULL_TEXT)); | |
| } | |
| @Test | |
| public void testSectionNameEmpty() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(EMPTY_STR, new Attributes()); | |
| mf = writeAndRead(mf); | |
| assertNotNull(mf.getEntries().get(EMPTY_STR)); | |
| } | |
| @Test | |
| public void testNamedSectionHeaderNameNull() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(SOME_KEY, new Attributes() {{ | |
| map.put(null, SOME_VALUE); | |
| }}); | |
| assertThrows(NullPointerException.class, () -> writeAndRead(mf)); | |
| } | |
| @Test | |
| public void testNamedSectionHeaderNameEmpty() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(SOME_KEY, new Attributes() {{ | |
| map.put(EMPTY_NAME, SOME_VALUE); | |
| }}); | |
| assertThrows(IOException.class, () -> writeAndRead(mf)); | |
| } | |
| @Test | |
| public void testNamedSectionHeaderValueNull() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(SOME_KEY, new Attributes() {{ | |
| map.put(new Name(SOME_KEY), null); | |
| }}); | |
| mf = writeAndRead(mf); | |
| assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY), | |
| NULL_TEXT); | |
| } | |
| @Test | |
| public void testNamedSectionHeaderValueEmpty() throws IOException { | |
| Manifest mf = new Manifest(); | |
| mf.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0"); | |
| mf.getEntries().put(SOME_KEY, new Attributes() {{ | |
| map.put(new Name(SOME_KEY), EMPTY_STR); | |
| }}); | |
| mf = writeAndRead(mf); | |
| assertEquals(mf.getEntries().get(SOME_KEY).getValue(SOME_KEY), | |
| EMPTY_STR); | |
| } | |
| static Manifest writeAndRead(Manifest mf) throws IOException { | |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |
| mf.write(out); | |
| byte[] mfBytes = out.toByteArray(); | |
| System.out.println("-".repeat(72)); | |
| System.out.print(new String(mfBytes, UTF_8)); | |
| System.out.println("-".repeat(72)); | |
| ByteArrayInputStream in = new ByteArrayInputStream(mfBytes); | |
| return new Manifest(in); | |
| } | |
| } |