package fr.mch.mdo.restaurant.services.logs; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Properties; import junit.framework.Test; import junit.framework.TestSuite; import fr.mch.mdo.i18n.IMessageQuery; import fr.mch.mdo.i18n.MessageQueryResourceBundleImpl; import fr.mch.mdo.logs.ILogger; import fr.mch.mdo.restaurant.resources.IResources; import fr.mch.mdo.test.MdoTestCase; /** * Coverage code is based on the loaded classes. So the Coverage code tools * takes into account the last loaded classes. The LoggerImpl uses the static * bloc for initialization. So we have to check this static code. In order to do * it, we have to reload the LoggerImpl class to check all possible * configurations. Because of LoggerImpl reloaded class(static initialization) * and because of technical coverage code issue, we have to create 2 types of * tests: 1) Test to really check all possible cases 2) Test to coverage code * * @author Mathieu */ public class LoggerImplTest extends MdoTestCase { /** * Create the test case * * @param testName * name of the test case */ public LoggerImplTest(String testName) { super(testName); } /** * @return the suite of tests being tested */ public static Test suite() { return new TestSuite(LoggerImplTest.class); } public void checkLoggerImpl(Class<?> loggerClass) { Class<?> clazz = loggerClass; if (clazz == null) { clazz = LoggerImpl.class; } // Constructor without argument ILogger logger = null; try { logger = (ILogger) clazz.getConstructor().newInstance(); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor without argument", logger); assertNull("Constructor without argument", logger.getLoggerMessage()); // Constructor with class name argument try { logger = (ILogger) clazz.getConstructor(String.class).newInstance(""); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor with class name argument", logger); assertNull("Constructor with class name argument", logger.getLoggerMessage()); // Constructor with ILoggerMessage argument IMessageQuery loggerMessage = new MessageQueryResourceBundleImpl(IResources.LOG_RESOURCE_BUNDLE_MESSAGES_FILE); try { logger = (ILogger) clazz.getConstructor(IMessageQuery.class).newInstance(loggerMessage); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor with ILoggerMessage argument", logger); assertNotNull("Constructor with ILoggerMessage argument", logger.getLoggerMessage()); // Constructor with ILoggerMessage argument and class name argument try { logger = (ILogger) clazz.getConstructor(IMessageQuery.class, String.class).newInstance(loggerMessage, ""); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor with ILoggerMessage argument and class name argument", logger); assertNotNull("Constructor with ILoggerMessage argument and class name argument", logger.getLoggerMessage()); } public void checkSetGetLoggerMessage(Class<?> loggerClass) { Class<?> clazz = loggerClass; if (clazz == null) { clazz = LoggerImpl.class; } // Constructor without argument ILogger logger = null; try { logger = (ILogger) clazz.getConstructor().newInstance(); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor without argument", logger); assertNull("Constructor without argument", logger.getLoggerMessage()); IMessageQuery loggerMessage = new MessageQueryResourceBundleImpl(IResources.LOG_RESOURCE_BUNDLE_MESSAGES_FILE); logger.setLoggerMessage(loggerMessage); assertNotNull("test setLoggerMessage", logger.getLoggerMessage()); } public void checkGetLogger(Class<?> loggerClass) { Class<?> clazz = loggerClass; if (clazz == null) { clazz = LoggerImpl.class; } // Constructor without argument ILogger logger = null; try { logger = (ILogger) clazz.getConstructor().newInstance(); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor without argument", logger); assertNull("Constructor without argument", logger.getLoggerMessage()); ILogger newLogger = logger.getLogger(); assertNotNull("New Logger not null", newLogger); assertNull("New Logger message is null", newLogger.getLoggerMessage()); IMessageQuery loggerMessage = new MessageQueryResourceBundleImpl(IResources.LOG_RESOURCE_BUNDLE_MESSAGES_FILE); logger.setLoggerMessage(loggerMessage); newLogger = logger.getLogger(); assertNotNull("New Logger not null", newLogger); assertNotNull("New Logger message is not null", newLogger.getLoggerMessage()); newLogger = logger.getLogger(""); assertNotNull("New Logger not null", newLogger); assertNotNull("New Logger message is not null", newLogger.getLoggerMessage()); } private Object invokeProcessQuery(final ILogger logger, final String query, final Object[] params) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class<?>[] argClasses = { String.class, Object[].class }; Object[] argObjects = { query, params }; if (params == null) { argClasses = new Class<?>[] { String.class }; argObjects = new Object[] { query }; } return invokeInstanceMethod(logger, "processQuery", argClasses, argObjects); } public void checkProcessQuery(Class<?> loggerClass) { String query = "query"; String[] params = null; Class<?> clazz = loggerClass; if (clazz == null) { clazz = LoggerImpl.class; } // Constructor without argument ILogger logger = null; try { logger = (ILogger) clazz.getConstructor().newInstance(); } catch (Exception e) { fail("Could not create new LoggerImpl instance"); } assertNotNull("Constructor without argument", logger); assertNull("Constructor without argument", logger.getLoggerMessage()); try { assertEquals("Invoke processQuery private method", query, invokeProcessQuery(logger, query, params)); params = new String[] { " " }; assertEquals("Invoke processQuery private method", query, invokeProcessQuery(logger, query, params)); } catch (InvocationTargetException e) { fail("Could not invoke processQuery private method"); } query = "my.key"; String value = "Impossible de sauver l''objet {0} : {1}"; Properties properties = new Properties(); properties.setProperty(query, value); // Add new properties updatePropertiesFile(LoggerImplTest.class, properties, false); // Set ILoggerMessage IMessageQuery loggerMessage = new MessageQueryResourceBundleImpl(LoggerImplTest.class.getName()); logger.setLoggerMessage(loggerMessage); assertNotNull("setLoggerMessage", logger.getLoggerMessage()); try { assertEquals("Invoke processQuery private method", value.replaceFirst("'", ""), invokeProcessQuery(logger, query, null)); params = new String[] { "a", "b" }; assertEquals("Invoke processQuery private method", value.replaceFirst("'", "").replaceFirst("\\{0\\}", "a").replaceFirst("\\{1\\}", "b"), invokeProcessQuery(logger, query, params)); } catch (InvocationTargetException e) { fail("Could not invoke processQuery private method"); } finally { // Remove new properties updatePropertiesFile(LoggerImplTest.class, properties, true); } } private Object invokeReload(final String configFile) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class<?>[] argClasses = { String.class }; Object[] argObjects = { configFile }; return invokeStaticMethod(LoggerImpl.class, "reload", argClasses, argObjects); } public void checkReload(Class<?> reloadedClass) { // Change the log4j configuration file String backupExtension = ".backup"; String propertiesExtension = ".properties"; String propertiesFile = IResources.class.getPackage().getName().replace(".", "/") + "/" + IResources.LOG4J_CONFIGURATION_FILE; renamePropertiesFileExtension(propertiesFile, backupExtension); try { if (reloadedClass == null) { // Reload the class LoggerImpl.class in order to take into // account the // new configuration file Class<?> clazz = super.reloadClass(LoggerImpl.class); // Load the class and create a new instance clazz.getConstructor(String.class).newInstance(LoggerImplTest.class.getName()); } } catch (Throwable e) { assertTrue("ExceptionInInitializerError when reloading the class LoggerImpl", e instanceof ExceptionInInitializerError); ExceptionInInitializerError nullPointerException = (ExceptionInInitializerError) e; assertTrue("ExceptionInInitializerError when reloading the class LoggerImpl", nullPointerException.getException() instanceof NullPointerException); } finally { // Rename back the log4j properties file propertiesFile = propertiesFile.replace(propertiesExtension, backupExtension); renamePropertiesFileExtension(propertiesFile, propertiesExtension); } try { invokeReload(null); } catch (InvocationTargetException e) { assertTrue("Could not invoke reload private method", e.getTargetException() instanceof NullPointerException); } // Check that the right properties file is loaded invokeLoggerLogByLogLevel(reloadedClass, "info", null, null); } private Class<?> invokeLoggerLogByLogLevel(Class<?> reloadedClass, String logLevelMethod, Object[] params, Throwable exception) { String propertiesFile = IResources.class.getPackage().getName().replace(".", "/") + "/" + IResources.LOG4J_CONFIGURATION_FILE; // Check that the right properties file is loaded File file = super.getFileFromClassPath(IResources.class, "logs/log4j/log4j.log"); Properties properties = updateLog4jPropertiesFile(file.getAbsolutePath(), logLevelMethod.toUpperCase()); // Update the configuration file and backup properties = super.updatePropertiesFile(propertiesFile, properties, false); Class<?> clazz = reloadedClass; // This will instantiate the LoggerImpl class in this class loader ILogger logger = new LoggerImpl(LoggerImplTest.class.getName()); try { if (clazz == null) { // Reload the class in order to take into account the new // properties // above for static fields or blocks clazz = reloadClass(LoggerImpl.class); } // Instantiate the loaded class, this will initialize the log4j // configuration for this JVM logger = (ILogger) clazz.getConstructor(String.class).newInstance(LoggerImplTest.class.getName()); } catch (Exception e) { fail("Could not instantiate the class LoggerImpl"); } assertNotNull("LoggerImpl instance must not be null", logger); if (reloadedClass == null) { assertTrue("The log file " + file.getAbsolutePath() + " must be empty ", file.length() == 0); } String message = "Hello World " + logLevelMethod; // Log the info message Class<?>[] argClasses = { String.class }; Object[] argObjects = { message }; if (params == null && exception != null) { argClasses = new Class<?>[] { String.class, Throwable.class }; message += " " + exception.getMessage(); argObjects = new Object[] { message, exception }; } else if (params != null && exception == null) { argClasses = new Class<?>[] { String.class, Object[].class }; message += " " + Arrays.toString(params); argObjects = new Object[] { message, params }; } else if (params != null && exception != null) { argClasses = new Class<?>[] { String.class, Object[].class, Throwable.class }; message += " " + Arrays.toString(params) + " " + exception.getMessage(); argObjects = new Object[] { message, params, exception }; } try { invokeInstanceMethod(logger, logLevelMethod, argClasses, argObjects); } catch (InvocationTargetException e) { fail("Could not invoke the logger method " + logLevelMethod + ": " + e.getTargetException()); } if (reloadedClass == null) { assertTrue("The log file " + file.getAbsolutePath() + " must not be empty", file.length() > 0); BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(file)); String line = bufferedReader.readLine(); assertNotNull("There is at least 1 line in the file", line); assertTrue("The line must contain the message", line.contains(message)); if (exception == null) { assertNull("There is only 1 line in the file", bufferedReader.readLine()); } } catch (Exception e) { fail("Could not open the logger file: " + e); } finally { try { bufferedReader.close(); } catch (Exception e) { fail("Could not close the logger file: " + e); } } } // Roll back old values super.updatePropertiesFile(propertiesFile, properties, false); return logger.getClass(); } private Properties updateLog4jPropertiesFile(String logFilePath, String logLevel) { Properties result = new Properties(); // Modify the file path in the properties files String key = "log4j.appender.A2.File"; String value = logFilePath; result.put(key, value); // The log file must be empty before to write in it key = "log4j.appender.A2.Append"; value = "false"; result.put(key, value); // Logger for LoggerImplTest class must be the logLevel argument key = "log4j.logger.fr.mch.mdo.restaurant.services.logs.LoggerImplTest"; value = logLevel; result.put(key, value); return result; } public Class<?> checkDebug(Class<?> reloadedClass) { Class<?> result = reloadedClass; // Test debug with 1 parameter result = invokeLoggerLogByLogLevel(reloadedClass, "debug", null, null); // Test debug with 2 parameters without Exception result = invokeLoggerLogByLogLevel(reloadedClass, "debug", new String[] { "1" }, null); // Test debug with 2 parameters with Exception result = invokeLoggerLogByLogLevel(reloadedClass, "debug", null, new NullPointerException("Logger Test in debug method")); // Test debug with 3 parameters result = invokeLoggerLogByLogLevel(reloadedClass, "debug", new String[] { "1" }, new NullPointerException("Logger Test in debug method")); return result; } public Class<?> checkInfo(Class<?> reloadedClass) { Class<?> result = reloadedClass; // Test info with 1 parameter result = invokeLoggerLogByLogLevel(reloadedClass, "info", null, null); // Test info with 2 parameters without Exception result = invokeLoggerLogByLogLevel(reloadedClass, "info", new String[] { "1" }, null); // Test info with 2 parameters with Exception result = invokeLoggerLogByLogLevel(reloadedClass, "info", null, new ClassCastException("Logger Test in info method")); // Test info with 3 parameters result = invokeLoggerLogByLogLevel(reloadedClass, "info", new String[] { "1" }, new ClassCastException("Logger Test in info method")); return result; } public Class<?> checkWarn(Class<?> reloadedClass) { Class<?> result = reloadedClass; // Test warn with 1 parameter result = invokeLoggerLogByLogLevel(reloadedClass, "warn", null, null); // Test warn with 2 parameters without Exception result = invokeLoggerLogByLogLevel(reloadedClass, "warn", new String[] { "1" }, null); // Test warn with 2 parameters with Exception result = invokeLoggerLogByLogLevel(reloadedClass, "warn", null, new NoClassDefFoundError("Logger Test in warn method")); // Test warn with 3 parameters result = invokeLoggerLogByLogLevel(reloadedClass, "warn", new String[] { "1" }, new NoClassDefFoundError("Logger Test in warn method")); return result; } public Class<?> checkError(Class<?> reloadedClass) { Class<?> result = reloadedClass; // Test error with 1 parameter result = invokeLoggerLogByLogLevel(reloadedClass, "error", null, null); // Test error with 2 parameters without Exception result = invokeLoggerLogByLogLevel(reloadedClass, "error", new String[] { "1" }, null); // Test error with 2 parameters with Exception result = invokeLoggerLogByLogLevel(reloadedClass, "error", null, new ExceptionInInitializerError("Logger Test in error method")); // Test error with 3 parameters result = invokeLoggerLogByLogLevel(reloadedClass, "error", new String[] { "1" }, new ExceptionInInitializerError("Logger Test in error method")); return result; } public Class<?> checkFatal(Class<?> reloadedClass) { Class<?> result = reloadedClass; // Test fatal with 1 parameter result = invokeLoggerLogByLogLevel(reloadedClass, "fatal", null, null); // Test fatal with 2 parameters without Exception result = invokeLoggerLogByLogLevel(reloadedClass, "fatal", new String[] { "1" }, null); // Test fatal with 2 parameters with Exception result = invokeLoggerLogByLogLevel(reloadedClass, "fatal", null, new NumberFormatException("Logger Test in fatal method")); // Test fatal with 3 parameters result = invokeLoggerLogByLogLevel(reloadedClass, "fatal", new String[] { "1" }, new NumberFormatException("Logger Test in fatal method")); return result; } /** * Coverage code is based on the loaded classes. So the Coverage code tools * takes into account the last loaded classes. The LoggerImpl uses the * static bloc for initialization. So we have to check this static code. In * order to do it, we have to reload the LoggerImpl class to check all * possible configurations. Because of LoggerImpl reloaded class(static * initialization) and because of technical coverage code issue, we have to * create 2 types of tests: 1) Test to really check all possible cases 2) * Test to coverage code */ public void testAll() { // 1) Test to really check all possible cases checkLoggerImpl(null); checkSetGetLoggerMessage(null); checkGetLogger(null); checkProcessQuery(null); checkReload(null); checkFatal(null); checkError(null); checkWarn(null); checkInfo(null); checkDebug(null); // 2) Test to coverage code // checkLoggerImpl(loggerClass); // checkSetGetLoggerMessage(loggerClass); // checkGetLogger(loggerClass); // checkProcessQuery(loggerClass); // checkReload(loggerClass); // checkDebug(loggerClass); // checkInfo(loggerClass); // checkWarn(loggerClass); // checkError(loggerClass); // checkFatal(loggerClass); } }