--- /dev/null 2017-03-31 13:13:18.000000000 +0100 +++ new/test/java/lang/System/LoggerFinder/LoggerFinderAPI/LoggerFinderAPI.java 2017-03-31 13:13:18.000000000 +0100 @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017, 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.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.lang.System.LoggerFinder; +import java.lang.reflect.Module; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; +// Can't use testng because testng requires java.logging +//import org.testng.annotations.Test; + +/** + * @test + * @bug 8177835 + * @summary Checks that the DefaultLoggerFinder and LoggingProviderImpl + * implementations of the System.LoggerFinder conform to the + * LoggerFinder specification, in particular with respect to + * throwing NullPointerException. The test uses --limit-module + * to force the selection of one or the other. + * @author danielfuchs + * @build LoggerFinderAPI + * @run main/othervm --limit-modules java.base,java.logging + * -Djava.util.logging.SimpleFormatter.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n + * LoggerFinderAPI + * @run main/othervm -Djdk.system.logger.format=LOG-%4$s:-[%2$s]-%5$s%6$s%n + * --limit-modules java.base + * LoggerFinderAPI + */ +public class LoggerFinderAPI { + + // Simplified log format string. No white space for main/othervm line + static final String TEST_FORMAT = "LOG-%4$s:-[%2$s]-%5$s%6$s%n"; + static final String JDK_FORMAT_PROP_KEY = "jdk.system.logger.format"; + static final String JUL_FORMAT_PROP_KEY = + "java.util.logging.SimpleFormatter.format"; + + static class RecordStream extends OutputStream { + static final Object LOCK = new Object[0]; + final PrintStream out; + final PrintStream err; + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + boolean record; + RecordStream(PrintStream out, PrintStream err) { + this.out = out; + this.err = err; + } + + @Override + public void write(int i) throws IOException { + if (record) { + bos.write(i); + out.write(i); + } else { + err.write(i); + } + } + + void startRecording() { + out.flush(); + err.flush(); + bos.reset(); + record = true; + } + byte[] stopRecording() { + out.flush(); + err.flush(); + record = false; + return bos.toByteArray(); + } + } + + static final PrintStream ERR = System.err; + static final PrintStream OUT = System.out; + static final RecordStream LOG_STREAM = new RecordStream(OUT, ERR); + static { + Locale.setDefault(Locale.US); + PrintStream perr = new PrintStream(LOG_STREAM); + System.setErr(perr); + } + + public static class MyResourceBundle extends ResourceBundle { + final ConcurrentHashMap map = new ConcurrentHashMap<>(); + @Override + protected Object handleGetObject(String string) { + return map.computeIfAbsent(string, s -> "[localized] " + s); + } + + @Override + public Enumeration getKeys() { + return map.keys(); + } + + } + + public static void main(String[] args) { + // Set on the command line, to ensure that the test will fail if + // the 'wrong' provider gets selected. + // System.setProperty(JDK_FORMAT_PROP_KEY, TEST_FORMAT); + // System.setProperty(JUL_FORMAT_PROP_KEY, TEST_FORMAT); + LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); + System.out.println("LoggerFinder is " + finder.getClass().getName()); + + LoggerFinderAPI apiTest = new LoggerFinderAPI(); + for (Object[] params : getLoggerDataProvider()) { + apiTest.testGetLogger((String)params[0], + (String)params[1], + (Module)params[2], + Throwable.class.getClass().cast(params[3])); + } + for (Object[] params : getLocalizedLoggerDataProvider()) { + apiTest.testGetLocalizedLogger((String)params[0], + (String)params[1], + (ResourceBundle)params[2], + (Module)params[3], + Throwable.class.getClass().cast(params[4])); + } + } + + //Can't use testng because testng requires java.logging + //@Test(dataProvider = "testGetLoggerDataProvider") + void testGetLogger(String desc, String name, Module mod, Class thrown) { + try { + LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); + Logger logger = finder.getLogger(name, mod); + if (thrown != null) { + throw new AssertionError("Exception " + thrown.getName() + + " not thrown for " + + "LoggerFinder.getLogger" + + " with " + desc); + } + // Make sure we don't fail if tests are run in parallel + synchronized(RecordStream.LOCK) { + LOG_STREAM.startRecording(); + try { + logger.log(Level.INFO, "{0} with {1}: PASSED", + "LoggerFinder.getLogger", + desc); + } finally { + byte[] logged = LOG_STREAM.stopRecording(); + check(logged, "testGetLogger", desc, null, + "LoggerFinder.getLogger"); + } + } + } catch (Throwable x) { + if (thrown != null && thrown.isInstance(x)) { + System.out.printf("Got expected exception for %s with %s: %s\n", + "LoggerFinder.getLogger", desc, String.valueOf(x)); + } else throw x; + } + } + + //Can't use testng because testng requires java.logging + //@Test(dataProvider = "getLocalizedLoggerDataProvider") + void testGetLocalizedLogger(String desc, String name, ResourceBundle bundle, + Module mod, Class thrown) { + try { + LoggerFinder finder = System.LoggerFinder.getLoggerFinder(); + Logger logger = finder.getLocalizedLogger(name, bundle, mod); + if (thrown != null) { + throw new AssertionError("Exception " + thrown.getName() + + " not thrown for " + + "LoggerFinder.getLocalizedLogger" + + " with " + desc); + } + // Make sure we don't fail if tests are run in parallel + synchronized(RecordStream.LOCK) { + LOG_STREAM.startRecording(); + try { + logger.log(Level.INFO, "{0} with {1}: PASSED", + "LoggerFinder.getLocalizedLogger", + desc); + } finally { + byte[] logged = LOG_STREAM.stopRecording(); + check(logged, "testGetLocalizedLogger", desc, bundle, + "LoggerFinder.getLocalizedLogger"); + } + } + } catch (Throwable x) { + if (thrown != null && thrown.isInstance(x)) { + System.out.printf("Got expected exception for %s with %s: %s\n", + "LoggerFinder.getLocalizedLogger", desc, String.valueOf(x)); + } else throw x; + } + } + + private void check(byte[] logged, String test, String desc, + ResourceBundle bundle, String meth) { + String msg = new String(logged); + String expected = String.format(TEST_FORMAT, null, + "LoggerFinderAPI " + test, null, Level.INFO.name(), + (bundle==null?"":"[localized] ") + meth + " with " + desc + ": PASSED", + ""); + if (!Objects.equals(msg, expected)) { + throw new AssertionError("Expected log message not found: " + + "\n\texpected: " + expected + + "\n\tretrieved: " + msg); + } + } + + + static final Module MODULE = LoggerFinderAPI.class.getModule(); + static final ResourceBundle BUNDLE = new MyResourceBundle(); + static final Object[][] GET_LOGGER = { + {"null name", null, MODULE , NullPointerException.class}, + {"null module", "foo", null, NullPointerException.class}, + {"null name and module", null, null, NullPointerException.class}, + {"non null name and module", "foo", MODULE, null}, + }; + static final Object[][] GET_LOCALIZED_LOGGER = { + {"null name", null, BUNDLE, MODULE , NullPointerException.class}, + {"null module", "foo", BUNDLE, null, NullPointerException.class}, + {"null name and module", null, BUNDLE, null, NullPointerException.class}, + {"non null name and module", "foo", BUNDLE, MODULE, null}, + {"null name and bundle", null, null, MODULE , NullPointerException.class}, + {"null module and bundle", "foo", null, null, NullPointerException.class}, + {"null name and module and bundle", null, null, null, NullPointerException.class}, + {"non null name and module, null bundle", "foo", null, MODULE, null}, + }; + public static Object[][] getLoggerDataProvider() { + return GET_LOGGER; + } + public static Object[][] getLocalizedLoggerDataProvider() { + return GET_LOCALIZED_LOGGER; + } +}