Source code for armi.tests.test_runLog

# Copyright 2019 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests of the runLog tooling."""
from io import StringIO
from shutil import rmtree
import logging
import os
import unittest

from armi import runLog
from armi.tests import mockRunLogs
from armi.utils.directoryChangers import TemporaryDirectoryChanger


[docs]class TestRunLog(unittest.TestCase):
[docs] def test_setVerbosityFromInteger(self): """Test that the log verbosity can be set with an integer.""" log = runLog._RunLog(1) expectedStrVerbosity = "debug" verbosityRank = log.getLogVerbosityRank(expectedStrVerbosity) runLog.setVerbosity(verbosityRank) self.assertEqual(verbosityRank, runLog.getVerbosity()) self.assertEqual(verbosityRank, logging.DEBUG)
[docs] def test_setVerbosityFromString(self): """Test that the log verbosity can be set with a string.""" log = runLog._RunLog(1) expectedStrVerbosity = "error" verbosityRank = log.getLogVerbosityRank(expectedStrVerbosity) runLog.setVerbosity(expectedStrVerbosity) self.assertEqual(verbosityRank, runLog.getVerbosity()) self.assertEqual(verbosityRank, logging.ERROR)
[docs] def test_verbosityOutOfRange(self): """Test that the log verbosity setting resets to a canonical value when it is out of range.""" runLog.setVerbosity(-50) self.assertEqual( runLog.LOG.logger.level, min([v[0] for v in runLog.LOG.logLevels.values()]) ) runLog.setVerbosity(5000) self.assertEqual( runLog.LOG.logger.level, max([v[0] for v in runLog.LOG.logLevels.values()]) )
[docs] def test_invalidSetVerbosityByString(self): """Test that the log verbosity setting fails if the integer is invalid.""" with self.assertRaises(KeyError): runLog.setVerbosity("taco") with self.assertRaises(TypeError): runLog.setVerbosity(["debug"])
[docs] def test_parentRunLogging(self): """A basic test of the logging of the parent runLog.""" # init the _RunLog object log = runLog.LOG = runLog._RunLog(0) log.startLog("test_parentRunLogging") runLog.createLogDir(0) log.setVerbosity(logging.INFO) # divert the logging to a stream, to make testing easier stream = StringIO() handler = logging.StreamHandler(stream) log.logger.handlers = [handler] # log some things log.log("debug", "You shouldn't see this.", single=False, label=None) log.log("warning", "Hello, ", single=False, label=None) log.log("error", "world!", single=False, label=None) log.logger.flush() log.logger.close() runLog.close(99) # test what was logged streamVal = stream.getvalue() self.assertIn("Hello", streamVal, msg=streamVal) self.assertIn("world", streamVal, msg=streamVal)
[docs] def test_warningReport(self): """A simple test of the warning tracking and reporting logic.""" # create the logger and do some logging log = runLog.LOG = runLog._RunLog(321) log.startLog("test_warningReport") runLog.createLogDir(0) # divert the logging to a stream, to make testing easier stream = StringIO() handler = logging.StreamHandler(stream) log.logger.handlers = [handler] # log some things log.setVerbosity(logging.INFO) log.log("warning", "test_warningReport", single=True, label=None) log.log("debug", "invisible due to log level", single=False, label=None) log.log("warning", "test_warningReport", single=True, label=None) log.log("error", "high level something", single=False, label=None) # test that the logging found some duplicate outputs dupsFilter = log.getDuplicatesFilter() self.assertIsNotNone(dupsFilter) warnings = dupsFilter.singleWarningMessageCounts self.assertGreater(len(warnings), 0) # run the warning report log.warningReport() runLog.close(1) runLog.close(0) # test what was logged streamVal = stream.getvalue() self.assertIn("test_warningReport", streamVal, msg=streamVal) self.assertIn("Final Warning Count", streamVal, msg=streamVal) self.assertNotIn("invisible", streamVal, msg=streamVal) self.assertEqual(streamVal.count("test_warningReport"), 2, msg=streamVal)
[docs] def test_warningReportInvalid(self): """A test of warningReport in an invalid situation.""" # create the logger and do some logging testName = "test_warningReportInvalid" log = runLog.LOG = runLog._RunLog(323) log.startLog(testName) runLog.createLogDir(0) # divert the logging to a stream, to make testing easier stream = StringIO() handler = logging.StreamHandler(stream) log.logger.handlers = [handler] # log some things log.setVerbosity(logging.INFO) log.log("warning", testName, single=True, label=None) log.log("debug", "invisible due to log level", single=False, label=None) log.log("warning", testName, single=True, label=None) log.log("error", "high level something", single=False, label=None) # test that the logging found some duplicate outputs def returnNone(*args, **kwargs): return None log.logger.getDuplicatesFilter = returnNone self.assertIsNone(log.logger.getDuplicatesFilter()) # run the warning report log.warningReport() runLog.close(1) runLog.close(0) # test what was logged streamVal = stream.getvalue() self.assertIn(testName, streamVal, msg=streamVal) self.assertIn("None Found", streamVal, msg=streamVal) self.assertNotIn("invisible", streamVal, msg=streamVal) self.assertEqual(streamVal.count(testName), 1, msg=streamVal)
[docs] def test_closeLogging(self): """A basic test of the close() functionality.""" def validate_loggers(log): """Little test helper, to make sure our loggers still look right.""" handlers = [str(h) for h in log.logger.handlers] self.assertEqual(len(handlers), 1, msg=",".join(handlers)) stderrHandlers = [str(h) for h in log.stderrLogger.handlers] self.assertEqual(len(stderrHandlers), 1, msg=",".join(stderrHandlers)) # init logger log = runLog.LOG = runLog._RunLog(777) validate_loggers(log) # start the logging for real log.startLog("test_closeLogging") runLog.createLogDir() validate_loggers(log) # close() and test that we have correctly nullified our loggers runLog.close(1) validate_loggers(log) # in a real run, the parent process would close() after all the children runLog.close(0)
[docs] def test_setVerbosity(self): """Let's test the setVerbosity() method carefully.""" with mockRunLogs.BufferLog() as mock: # we should start with a clean slate self.assertEqual("", mock.getStdout()) runLog.LOG.startLog("test_setVerbosity") runLog.LOG.setVerbosity(logging.INFO) # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.INFO) runLog.info("hi") self.assertIn("hi", mock.getStdout()) mock.emptyStdout() runLog.debug("invisible") self.assertEqual("", mock.getStdout()) # setVerbosity() to WARNING, and verify it is working runLog.LOG.setVerbosity(logging.WARNING) runLog.info("still invisible") self.assertEqual("", mock.getStdout()) runLog.warning("visible") self.assertIn("visible", mock.getStdout()) mock.emptyStdout() # setVerbosity() to DEBUG, and verify it is working runLog.LOG.setVerbosity(logging.DEBUG) runLog.debug("Visible") self.assertIn("Visible", mock.getStdout()) mock.emptyStdout() # setVerbosity() to ERROR, and verify it is working runLog.LOG.setVerbosity(logging.ERROR) runLog.warning("Still Invisible") self.assertEqual("", mock.getStdout()) runLog.error("Visible!") self.assertIn("Visible!", mock.getStdout()) # we shouldn't be able to setVerbosity() to a non-canonical value (logging module defense) self.assertEqual(runLog.LOG.getVerbosity(), logging.ERROR) runLog.LOG.setVerbosity(logging.WARNING + 1) self.assertEqual(runLog.LOG.getVerbosity(), logging.WARNING)
[docs] def test_setVerbosityBeforeStartLog(self): """The user/dev may accidentally call ``setVerbosity()`` before ``startLog()``, this should be mostly supportable.""" with mockRunLogs.BufferLog() as mock: # we should start with a clean slate self.assertEqual("", mock.getStdout()) runLog.LOG.setVerbosity(logging.DEBUG) runLog.LOG.startLog("test_setVerbosityBeforeStartLog") # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.DEBUG) runLog.debug("hi") self.assertIn("hi", mock.getStdout()) mock.emptyStdout()
[docs] def test_callingStartLogMultipleTimes(self): """Calling startLog() multiple times will lead to multiple output files, but logging should still work.""" with mockRunLogs.BufferLog() as mock: # we should start with a clean slate self.assertEqual("", mock.getStdout()) runLog.LOG.startLog("test_callingStartLogMultipleTimes1") runLog.LOG.setVerbosity(logging.INFO) # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.INFO) runLog.info("hi1") self.assertIn("hi1", mock.getStdout()) mock.emptyStdout() # call startLog() again runLog.LOG.startLog("test_callingStartLogMultipleTimes2") runLog.LOG.setVerbosity(logging.INFO) # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.INFO) runLog.info("hi2") self.assertIn("hi2", mock.getStdout()) mock.emptyStdout() # call startLog() again runLog.LOG.startLog("test_callingStartLogMultipleTimes3") runLog.LOG.setVerbosity(logging.INFO) # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.INFO) runLog.info("hi3") self.assertIn("hi3", mock.getStdout()) mock.emptyStdout() # call startLog() again, with a duplicate logger name runLog.LOG.startLog("test_callingStartLogMultipleTimes3") runLog.LOG.setVerbosity(logging.INFO) # we should start at info level, and that should be working correctly self.assertEqual(runLog.LOG.getVerbosity(), logging.INFO) runLog.info("hi333") self.assertIn("hi333", mock.getStdout()) mock.emptyStdout()
[docs] def test_concatenateLogs(self): """Simple test of the concat logs function.""" with TemporaryDirectoryChanger(): # create the log dir logDir = "test_concatenateLogs" if os.path.exists(logDir): rmtree(logDir) runLog.createLogDir(logDir) # create as stdout file stdoutFile1 = os.path.join( logDir, "{}.runLogTest.0000.stdout".format(runLog.STDOUT_LOGGER_NAME) ) with open(stdoutFile1, "w") as f: f.write("hello world\n") stdoutFile2 = os.path.join( logDir, "{}.runLogTest.0001.stdout".format(runLog.STDOUT_LOGGER_NAME) ) with open(stdoutFile2, "w") as f: f.write("hello other world\n") self.assertTrue(os.path.exists(stdoutFile1)) self.assertTrue(os.path.exists(stdoutFile2)) # create a stderr file stderrFile = os.path.join( logDir, "{}.runLogTest.0000.stderr".format(runLog.STDOUT_LOGGER_NAME) ) with open(stderrFile, "w") as f: f.write("goodbye cruel world\n") self.assertTrue(os.path.exists(stderrFile)) # concat logs runLog.concatenateLogs(logDir=logDir) # verify output combinedLogFile = os.path.join(logDir, "runLogTest-mpi.log") self.assertTrue(os.path.exists(combinedLogFile)) self.assertFalse(os.path.exists(stdoutFile1)) self.assertFalse(os.path.exists(stdoutFile2)) self.assertFalse(os.path.exists(stderrFile))
[docs] def test_createLogDir(self): """Test the createLogDir() method.""" with TemporaryDirectoryChanger(): logDir = "test_createLogDir" self.assertFalse(os.path.exists(logDir)) for _ in range(10): runLog.createLogDir(logDir) self.assertTrue(os.path.exists(logDir))
[docs]class TestRunLogger(unittest.TestCase): def setUp(self): self.rl = runLog.RunLogger("ARMI|things_and_stuff|0")
[docs] def test_getDuplicatesFilter(self): df = self.rl.getDuplicatesFilter() self.assertEqual(type(df), runLog.DeduplicationFilter) self.rl.filters = [] self.assertIsNone(self.rl.getDuplicatesFilter())
[docs] def test_allowStopDuplicates(self): # the usual case, where the DeduplicateFilter already exists self.assertEqual(len(self.rl.filters), 1) self.rl.allowStopDuplicates() self.assertEqual(len(self.rl.filters), 1) # the unusual case, where the DeduplicateFilter isn't there self.rl.filters = [] self.assertEqual(len(self.rl.filters), 0) self.rl.allowStopDuplicates() self.assertEqual(len(self.rl.filters), 1)
[docs] def test_write(self): # divert the logging to a stream, to make testing easier stream = StringIO() handler = logging.StreamHandler(stream) self.rl.handlers = [handler] # log some things testName = "test_write" self.rl.write(testName) # test what was logged streamVal = stream.getvalue() self.assertIn(testName, streamVal, msg=streamVal)