#region Apache License // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to you 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. // #endregion using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Text; using System.Text.RegularExpressions; using log4net.Appender; using log4net.Core; using log4net.Layout; using log4net.Repository.Hierarchy; using log4net.Util; using NUnit.Framework; using System.Globalization; namespace log4net.Tests.Appender { /// /// Used for internal unit testing the class. /// [TestFixture] public class RollingFileAppenderTest { private const string c_fileName = "test_41d3d834_4320f4da.log"; private const string c_testMessage98Chars = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567"; private const string c_testMessage99Chars = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"; private const int c_iMaximumFileSize = 450; // in bytes private int _iMessagesLoggedThisFile = 0; private int _iMessagesLogged = 0; private int _iCountDirection = 0; private int _MaxSizeRollBackups = 3; private CountingAppender _caRoot; private Logger _root; private CultureInfo _currentCulture; private CultureInfo _currentUICulture; private class SilentErrorHandler : IErrorHandler { private StringBuilder m_buffer = new StringBuilder(); public string Message { get { return m_buffer.ToString(); } } public void Error(string message) { m_buffer.Append(message + "\n"); } public void Error(string message, Exception e) { m_buffer.Append(message + "\n" + e.Message + "\n"); } public void Error(string message, Exception e, ErrorCode errorCode) { m_buffer.Append(message + "\n" + e.Message + "\n"); } } /// /// Sets up variables used for the tests /// private void InitializeVariables() { _iMessagesLoggedThisFile = 0; _iMessagesLogged = 0; _iCountDirection = +1; // Up _MaxSizeRollBackups = 3; } /// /// Shuts down any loggers in the hierarchy, along /// with all appenders, and deletes any test files used /// for logging. /// private static void ResetAndDeleteTestFiles() { // Regular users should not use the clear method lightly! LogManager.GetRepository().ResetConfiguration(); LogManager.GetRepository().Shutdown(); ((Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Clear(); DeleteTestFiles(); } /// /// Any initialization that happens before each test can /// go here /// [SetUp] public void SetUp() { ResetAndDeleteTestFiles(); InitializeVariables(); // set correct thread culture _currentCulture = System.Threading.Thread.CurrentThread.CurrentCulture; _currentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; System.Threading.Thread.CurrentThread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; } /// /// Any steps that happen after each test go here /// [TearDown] public void TearDown() { ResetAndDeleteTestFiles(); // restore previous culture System.Threading.Thread.CurrentThread.CurrentCulture = _currentCulture; System.Threading.Thread.CurrentThread.CurrentUICulture = _currentUICulture; } /// /// Finds the number of files that match the base file name, /// and matches the result against an expected count /// /// private static void VerifyFileCount(int iExpectedCount) { ArrayList alFiles = GetExistingFiles(c_fileName); Assert.IsNotNull(alFiles); Assert.AreEqual(iExpectedCount, alFiles.Count); } /// /// Creates a file with the given number, and the shared base file name /// /// private static void CreateFile(int iFileNumber) { FileInfo fileInfo = new FileInfo(MakeFileName(c_fileName, iFileNumber)); FileStream fileStream = null; try { fileStream = fileInfo.Create(); } finally { if (null != fileStream) { try { fileStream.Close(); } catch { } } } } /// /// Verifies that the code correctly loads all filenames /// [Test] public void TestGetExistingFiles() { VerifyFileCount(0); CreateFile(0); VerifyFileCount(1); CreateFile(1); VerifyFileCount(2); } /// /// Removes all test files that exist /// private static void DeleteTestFiles() { ArrayList alFiles = GetExistingFiles(c_fileName); foreach(string sFile in alFiles) { try { Debug.WriteLine("Deleting test file " + sFile); File.Delete(sFile); } catch(Exception ex) { Debug.WriteLine("Exception while deleting test file " + ex); } } } ///// ///// Generates a file name associated with the count. ///// ///// ///// //private string MakeFileName(int iFileCount) //{ // return MakeFileName(_fileName, iFileCount); //} /// /// Generates a file name associated with the count, using /// the base file name. /// /// /// /// private static string MakeFileName(string sBaseFile, int iFileCount) { if (0 == iFileCount) { return sBaseFile; } return sBaseFile + "." + iFileCount; } /// /// Returns a RollingFileAppender using all the internal settings for maximum /// file size and number of backups /// /// private RollingFileAppender CreateAppender() { return CreateAppender(new FileAppender.ExclusiveLock()); } /// /// Returns a RollingFileAppender using all the internal settings for maximum /// file size and number of backups /// /// The locking model to test /// private RollingFileAppender CreateAppender(FileAppender.LockingModelBase lockModel) { // // Use a basic pattern that // includes just the message and a CR/LF. // PatternLayout layout = new PatternLayout("%m%n"); // // Create the new appender // RollingFileAppender appender = new RollingFileAppender(); appender.Layout = layout; appender.File = c_fileName; appender.Encoding = Encoding.ASCII; appender.MaximumFileSize = c_iMaximumFileSize.ToString(); appender.MaxSizeRollBackups = _MaxSizeRollBackups; appender.CountDirection = _iCountDirection; appender.RollingStyle = RollingFileAppender.RollingMode.Size; appender.LockingModel = lockModel; appender.ActivateOptions(); return appender; } /// /// Used for test purposes, a table of these objects can be used to identify /// any existing files and their expected length. /// public class RollFileEntry { /// /// Stores the name of the file /// private string m_fileName; /// /// The expected length of the file /// private long m_fileLength; /// /// Default constructor /// public RollFileEntry() { } /// /// Constructor used when the fileInfo and expected length are known /// /// /// public RollFileEntry(string fileName, long fileLength) { m_fileName = fileName; m_fileLength = fileLength; } /// /// Stores the name of the file /// public string FileName { get { return m_fileName; } } /// /// The expected length of the file /// public long FileLength { get { return m_fileLength; } } } /// /// Used for table-driven testing. This class holds information that can be used /// for testing of file rolling. /// public class RollConditions { /// /// A table of entries showing files that should exist and their expected sizes /// before logging is called /// private RollFileEntry[] m_preLogFileEntries; /// /// A table of entries showing files that should exist and their expected sizes /// after a message is logged /// private RollFileEntry[] m_postLogFileEntries; /// /// Constructor, taking all required parameters /// /// /// public RollConditions(RollFileEntry[] preLogFileEntries, RollFileEntry[] postLogFileEntries) { m_preLogFileEntries = preLogFileEntries; m_postLogFileEntries = postLogFileEntries; } /// /// A table of entries showing files that should exist and their expected sizes /// before logging is called /// public RollFileEntry[] GetPreLogFileEntries() { return m_preLogFileEntries; } /// /// A table of entries showing files that should exist and their expected sizes /// after a message is logged /// public RollFileEntry[] GetPostLogFileEntries() { return m_postLogFileEntries; } } private static void VerifyExistenceAndRemoveFromList(ArrayList alExisting, string sFileName, FileInfo file, RollFileEntry entry) { Assert.IsTrue(alExisting.Contains(sFileName), "filename {0} not found in test directory", sFileName); Assert.AreEqual(entry.FileLength, file.Length, "file length mismatch"); // Remove this file from the list alExisting.Remove(sFileName); } /// /// Checks that all the expected files exist, and only the expected files. Also /// verifies the length of all files against the expected length /// /// /// private static void VerifyFileConditions(string sBaseFileName, RollFileEntry[] fileEntries) { ArrayList alExisting = GetExistingFiles(sBaseFileName); if (null != fileEntries) { // AssertEquals( "File count mismatch", alExisting.Count, fileEntries.Length ); foreach(RollFileEntry rollFile in fileEntries) { string sFileName = rollFile.FileName; FileInfo file = new FileInfo(sFileName); if (rollFile.FileLength > 0) { Assert.IsTrue(file.Exists, "filename {0} does not exist", sFileName); VerifyExistenceAndRemoveFromList(alExisting, sFileName, file, rollFile); } else { // If length is 0, file may not exist yet. If file exists, make sure length // is zero. If file doesn't exist, this is OK if (file.Exists) { VerifyExistenceAndRemoveFromList(alExisting, sFileName, file, rollFile); } } } } else { Assert.AreEqual(0, alExisting.Count); } // This check ensures no extra files matching the wildcard pattern exist. // We only want the files we expect, and no others Assert.AreEqual(0, alExisting.Count); } /// /// Called before logging a message to check that all the expected files exist, /// and only the expected files. Also verifies the length of all files against /// the expected length /// /// /// private static void VerifyPreConditions(string sBaseFileName, RollConditions entry) { VerifyFileConditions(sBaseFileName, entry.GetPreLogFileEntries()); } /// /// Called after logging a message to check that all the expected files exist, /// and only the expected files. Also verifies the length of all files against /// the expected length /// /// /// private static void VerifyPostConditions(string sBaseFileName, RollConditions entry) { VerifyFileConditions(sBaseFileName, entry.GetPostLogFileEntries()); } /// /// Logs a message, verifying the expected message counts against the /// current running totals. /// /// /// private void LogMessage(RollConditions entry, string sMessageToLog) { Assert.AreEqual(_caRoot.Counter, _iMessagesLogged++); _root.Log(Level.Debug, sMessageToLog, null); Assert.AreEqual(_caRoot.Counter, _iMessagesLogged); _iMessagesLoggedThisFile++; } //private void DumpFileEntry( RollFileEntry entry ) //{ // System.Diagnostics.Debug.WriteLine( "\tfile name: " + entry.FileName ); // System.Diagnostics.Debug.WriteLine( "\tfile length: " + entry.FileLength ); //} //private void DumpTableEntry( RollConditions entry ) //{ // System.Diagnostics.Debug.WriteLine( "Pre-Conditions" ); // foreach( RollFileEntry file in entry.GetPreLogFileEntries() ) // { // DumpFileEntry( file ); // } // System.Diagnostics.Debug.WriteLine( "Post-Conditions" ); // foreach( RollFileEntry file in entry.GetPostLogFileEntries() ) // { // DumpFileEntry( file ); // } // // System.Diagnostics.Debug.WriteLine(""); //} /// /// Runs through all table entries, logging messages. Before each message is logged, /// pre-conditions are checked to ensure the expected files exist and they are the /// expected size. After logging, verifies the same. /// /// /// /// private void RollFromTableEntries(string sBaseFileName, RollConditions[] entries, string sMessageToLog) { for(int i = 0; i < entries.Length; i++) { RollConditions entry = entries[i]; // System.Diagnostics.Debug.WriteLine( i + ": Entry " + i + " pre/post conditions"); // DumpTableEntry( entry ); // System.Diagnostics.Debug.WriteLine( i + ": Testing entry pre-conditions"); VerifyPreConditions(sBaseFileName, entry); // System.Diagnostics.Debug.WriteLine( i + ": Logging message"); LogMessage(entry, sMessageToLog); // System.Diagnostics.Debug.WriteLine( i + ": Testing entry post-conditions"); VerifyPostConditions(sBaseFileName, entry); // System.Diagnostics.Debug.WriteLine( i + ": Finished validating entry\n"); } } private static readonly int s_Newline_Length = Environment.NewLine.Length; /// /// Returns the number of bytes logged per message, including /// any newline characters in addition to the message length. /// /// /// private static int TotalMessageLength(string sMessage) { return sMessage.Length + s_Newline_Length; } /// /// Determines how many messages of a fixed length can be logged /// to a single file before the file rolls. /// /// /// private static int MessagesPerFile(int iMessageLength) { int iMessagesPerFile = c_iMaximumFileSize / iMessageLength; // // RollingFileAppender checks for wrap BEFORE logging, // so we will actually get one more message per file than // we would otherwise. // if (iMessagesPerFile * iMessageLength < c_iMaximumFileSize) { iMessagesPerFile++; } return iMessagesPerFile; } /// /// Determines the name of the current file /// /// private static string GetCurrentFile() { // Current file name is always the base file name when // counting. Dates will need a different approach return c_fileName; } /// /// Turns a group of file names into an array of file entries that include the name /// and a size. This is useful for assigning the properties of backup files, when /// the length of the files are all the same size due to a fixed message length. /// /// /// /// private static RollFileEntry[] MakeBackupFileEntriesFromBackupGroup(string sBackupGroup, int iBackupFileLength) { string[] sFiles = sBackupGroup.Split(' '); ArrayList alEntries = new ArrayList(); for(int i = 0; i < sFiles.Length; i++) { // Weed out any whitespace entries from the array if (sFiles[i].Trim().Length > 0) { alEntries.Add(new RollFileEntry(sFiles[i], iBackupFileLength)); } } return (RollFileEntry[])alEntries.ToArray(typeof(RollFileEntry)); } /// /// Finds the iGroup group in the string (comma separated groups) /// /// /// /// private static string GetBackupGroup(string sBackupGroups, int iGroup) { string[] sGroups = sBackupGroups.Split(','); return sGroups[iGroup]; } ///// ///// Builds a collection of file entries based on the file names ///// specified in a groups string and the max file size from the ///// stats object ///// ///// ///// ///// //private RollFileEntry[] MakeBackupFileEntriesForPreCondition( string sBackupGroups, RollingStats stats ) //{ // if (0 == stats.NumberOfFileRolls ) // { // return null; // first round has no previous backups // } // string sGroup; // if (0 == stats.MessagesThisFile ) // { // // first file has special pattern...since rolling doesn't occur when message // // is logged, rather before next message is logged. // if (stats.NumberOfFileRolls <= 1 ) // { // return null; // } // // Use backup files from previous round. The minus 2 is because we have already // // rolled, and the first round uses null instead of the string // sGroup = GetBackupGroup( sBackupGroups, stats.NumberOfFileRolls-2 ); // } // else // { // sGroup = GetBackupGroup( sBackupGroups, stats.NumberOfFileRolls-1 ); // } // return MakeBackupFileEntriesFromBackupGroup( sGroup, stats.MaximumFileSize ); //} /// /// Builds a collection of file entries based on the file names /// specified in a groups string and the max file size from the /// stats object /// /// /// /// private static RollFileEntry[] MakeBackupFileEntriesForPostCondition(string sBackupGroups, RollingStats stats) { if (0 == stats.NumberOfFileRolls) { return null; // first round has no previous backups } string sGroup = GetBackupGroup(sBackupGroups, stats.NumberOfFileRolls - 1); return MakeBackupFileEntriesFromBackupGroup(sGroup, stats.MaximumFileSize); } /// /// This class holds information that is used while we are generating /// test data sets /// public class RollingStats { private int iTotalMessageLength; private int iMessagesPerFile; private int iMessagesThisFile; private int iNumberOfFileRolls; /// /// Number of total bytes a log file can reach. /// public int MaximumFileSize { get { return TotalMessageLength * MessagesPerFile; } } /// /// The length of a message, including any CR/LF characters. /// This length assumes all messages are a fixed length for /// test purposes. /// public int TotalMessageLength { get { return iTotalMessageLength; } set { iTotalMessageLength = value; } } /// /// A count of the number of messages that are logged to each /// file. /// public int MessagesPerFile { get { return iMessagesPerFile; } set { iMessagesPerFile = value; } } /// /// Counts how many messages have been logged to the current file /// public int MessagesThisFile { get { return iMessagesThisFile; } set { iMessagesThisFile = value; } } /// /// Counts how many times a file roll has occurred /// public int NumberOfFileRolls { get { return iNumberOfFileRolls; } set { iNumberOfFileRolls = value; } } } /// /// The stats are used to keep track of progress while we are algorithmically /// generating a table of pre/post condition tests for file rolling. /// /// /// private static RollingStats InitializeStats(string sTestMessage) { RollingStats rollingStats = new RollingStats(); rollingStats.TotalMessageLength = TotalMessageLength(sTestMessage); rollingStats.MessagesPerFile = MessagesPerFile(rollingStats.TotalMessageLength); rollingStats.MessagesThisFile = 0; rollingStats.NumberOfFileRolls = 0; return rollingStats; } /// /// Takes an existing array of RollFileEntry objects, creates a new array one element /// bigger, and appends the final element to the end. If the existing entries are /// null (no entries), then a one-element array is returned with the final element /// as the only entry. /// /// /// /// private static RollFileEntry[] AddFinalElement(RollFileEntry[] existing, RollFileEntry final) { int iLength = 1; if (null != existing) { iLength += existing.Length; } RollFileEntry[] combined = new RollFileEntry[iLength]; if (null != existing) { Array.Copy(existing, 0, combined, 0, existing.Length); } combined[iLength - 1] = final; return combined; } /// /// Generates the pre and post condition arrays from an array of backup files and the /// current file / next file. /// /// /// /// /// /// /// private static RollConditions BuildTableEntry(string sBackupFiles, RollConditions preCondition, RollFileEntry current, RollFileEntry currentNext, RollingStats rollingStats) { RollFileEntry[] backupsPost = MakeBackupFileEntriesForPostCondition(sBackupFiles, rollingStats); RollFileEntry[] post = AddFinalElement(backupsPost, currentNext); if (null == preCondition) { return new RollConditions(AddFinalElement(null, current), post); } return new RollConditions(preCondition.GetPostLogFileEntries(), post); } /// /// Returns a RollFileEntry that represents the next state of the current file, /// based on the current state. When the current state would roll, the next /// entry is the current file wrapped to 0 bytes. Otherwise, the next state /// is the post-condition passed in as the currentNext parameter /// /// /// /// private static RollFileEntry MoveNextEntry(RollingStats rollingStats, RollFileEntry currentNext) { rollingStats.MessagesThisFile = rollingStats.MessagesThisFile + 1; if (rollingStats.MessagesThisFile >= rollingStats.MessagesPerFile) { rollingStats.MessagesThisFile = 0; rollingStats.NumberOfFileRolls = rollingStats.NumberOfFileRolls + 1; return new RollFileEntry(GetCurrentFile(), 0); } else { return currentNext; } } /// /// Callback point for the regular expression parser. Turns /// the number into a file name. /// /// /// private static string NumberedNameMaker(Match match) { Int32 iValue = Int32.Parse(match.Value); return MakeFileName(c_fileName, iValue); } /// /// Parses a numeric list of files, turning them into file names. /// Calls back to a method that does the actual replacement, turning /// the numeric value into a filename. /// /// /// /// private static string ConvertToFiles(string sBackupInfo, MatchEvaluator evaluator) { Regex regex = new Regex(@"\d+"); return regex.Replace(sBackupInfo, evaluator); } /// /// Makes test entries used for verifying counted file names /// /// A message to log repeatedly /// Filename groups used to indicate backup file name progression /// that results after each message is logged /// How many times the test message will be repeatedly logged /// private static RollConditions[] MakeNumericTestEntries(string sTestMessage, string sBackupInfo, int iMessagesToLog) { return MakeTestEntries( sTestMessage, sBackupInfo, iMessagesToLog, new MatchEvaluator(NumberedNameMaker)); } /// /// This routine takes a list of backup file names and a message that will be logged /// repeatedly, and generates a collection of objects containing pre-condition and /// post-condition information. This pre/post information shows the names and expected /// file sizes for all files just before and just after a message is logged. /// /// A message to log repeatedly /// Filename groups used to indicate backup file name progression /// that results after each message is logged /// How many times the test message will be repeatedly logged /// Function that can turn a number into a filename /// private static RollConditions[] MakeTestEntries(string sTestMessage, string sBackupInfo, int iMessagesToLog, MatchEvaluator evaluator) { string sBackupFiles = ConvertToFiles(sBackupInfo, evaluator); RollConditions[] table = new RollConditions[iMessagesToLog]; RollingStats rollingStats = InitializeStats(sTestMessage); RollConditions preCondition = null; rollingStats.MessagesThisFile = 0; RollFileEntry currentFile = new RollFileEntry(GetCurrentFile(), 0); for(int i = 0; i < iMessagesToLog; i++) { RollFileEntry currentNext = new RollFileEntry( GetCurrentFile(), (1 + rollingStats.MessagesThisFile) * rollingStats.TotalMessageLength); table[i] = BuildTableEntry(sBackupFiles, preCondition, currentFile, currentNext, rollingStats); preCondition = table[i]; //System.Diagnostics.Debug.WriteLine( "Message " + i ); //DumpTableEntry( table[i] ); currentFile = MoveNextEntry(rollingStats, currentNext); } return table; } /// /// Uses the externally defined rolling table to verify rolling names/sizes /// /// /// Pattern is: check pre-conditions. Log messages, checking size of current file. /// when size exceeds limit, check post conditions. Can determine from message the /// number of messages N that will cause a roll to occur. Challenge is to verify the /// expected files, their sizes, and the names. For a message of length L, the backups /// will be of size (N * L), and the current file will be of size (K * L), where K is /// the number of messages that have been logged to this file. /// /// File sizes can be checked algorithmically. /// /// File names are generated using a table driven algorithm, where a number is turned into /// the actual filename. /// /// The entries are comma-separated, with spaces between the names. Each comma indicates /// a 'roll', and the group between commas indicates the numbers for all backup files that /// occur as a result of the roll. It is assumed that no backup files exist before a roll /// occurs /// /// private void VerifyRolling(RollConditions[] table) { ConfigureRootAppender(); RollFromTableEntries(c_fileName, table, GetTestMessage()); } /// /// Validates rolling using a fixed number of backup files, with /// count direction set to up, so that newer files have higher counts. /// Newest = N, Oldest = N-K, where K is the number of backups to allow /// and N is the number of times rolling has occurred. /// [Test] public void TestRollingCountUpFixedBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = "1, 1 2, 1 2 3, 2 3 4, 3 4 5"; // // Count Up // _iCountDirection = +1; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Validates rolling using an infinite number of backup files, with /// count direction set to up, so that newer files have higher counts. /// Newest = N, Oldest = 1, where N is the number of times rolling has /// occurred. /// [Test] public void TestRollingCountUpInfiniteBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = "1, 1 2, 1 2 3, 1 2 3 4, 1 2 3 4 5"; // // Count Up // _iCountDirection = +1; // // Infinite backups // _MaxSizeRollBackups = -1; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Validates rolling with no backup files, with count direction set to up. /// Only the current file should be present, wrapping to 0 bytes once the /// previous file fills up. /// [Test] public void TestRollingCountUpZeroBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = ", , , , "; // // Count Up // _iCountDirection = +1; // // No backups // _MaxSizeRollBackups = 0; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Validates rolling using a fixed number of backup files, with /// count direction set to down, so that older files have higher counts. /// Newest = 1, Oldest = N, where N is the number of backups to allow /// [Test] public void TestRollingCountDownFixedBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = "1, 1 2, 1 2 3, 1 2 3, 1 2 3"; // // Count Up // _iCountDirection = -1; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Validates rolling using an infinite number of backup files, with /// count direction set to down, so that older files have higher counts. /// Newest = 1, Oldest = N, where N is the number of times rolling has /// occurred /// [Test] public void TestRollingCountDownInfiniteBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = "1, 1 2, 1 2 3, 1 2 3 4, 1 2 3 4 5"; // // Count Down // _iCountDirection = -1; // // Infinite backups // _MaxSizeRollBackups = -1; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Validates rolling with no backup files, with count direction set to down. /// Only the current file should be present, wrapping to 0 bytes once the /// previous file fills up. /// [Test] public void TestRollingCountDownZeroBackups() { // // Oldest to newest when reading in a group left-to-right, so 1 2 3 means 1 is the // oldest, and 3 is the newest // string sBackupInfo = ", , , , "; // // Count Up // _iCountDirection = -1; // // No backups // _MaxSizeRollBackups = 0; // // Log 30 messages. This is 5 groups, 6 checks per group ( 0, 100, 200, 300, 400, 500 // bytes for current file as messages are logged. // int iMessagesToLog = 30; VerifyRolling(MakeNumericTestEntries(GetTestMessage(), sBackupInfo, iMessagesToLog)); } /// /// Configures the root appender for counting and rolling /// private void ConfigureRootAppender() { _root = ((Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root; _root.Level = Level.Debug; _caRoot = new CountingAppender(); _root.AddAppender(_caRoot); Assert.AreEqual(_caRoot.Counter, 0); // // Set the root appender with a RollingFileAppender // _root.AddAppender(CreateAppender()); _root.Repository.Configured = true; } /// /// Verifies that the current backup index is detected correctly when initializing /// /// /// /// private static void VerifyInitializeRollBackupsFromBaseFile(string sBaseFile, ArrayList alFiles, int iExpectedCurSizeRollBackups) { InitializeAndVerifyExpectedValue(alFiles, sBaseFile, CreateRollingFileAppender("5,0,1"), iExpectedCurSizeRollBackups); } /// /// Tests that the current backup index is 0 when no /// existing files are seen /// [Test] public void TestInitializeRollBackups1() { string sBaseFile = "LogFile.log"; ArrayList arrFiles = new ArrayList(); arrFiles.Add("junk1"); arrFiles.Add("junk1.log"); arrFiles.Add("junk2.log"); arrFiles.Add("junk.log.1"); arrFiles.Add("junk.log.2"); int iExpectedCurSizeRollBackups = 0; VerifyInitializeRollBackupsFromBaseFile(sBaseFile, arrFiles, iExpectedCurSizeRollBackups); } /// /// Verifies that files are detected when the base file is specified /// /// private static void VerifyInitializeRollBackupsFromBaseFile(string sBaseFile) { ArrayList alFiles = MakeTestDataFromString(sBaseFile, "0,1,2"); int iExpectedCurSizeRollBackups = 2; VerifyInitializeRollBackupsFromBaseFile(sBaseFile, alFiles, iExpectedCurSizeRollBackups); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountUpFixed() { ArrayList alFiles = MakeTestDataFromString("3,4,5"); int iExpectedValue = 5; InitializeAndVerifyExpectedValue(alFiles, c_fileName, CreateRollingFileAppender("3,0,1"), iExpectedValue); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountUpFixed2() { ArrayList alFiles = MakeTestDataFromString("0,3"); int iExpectedValue = 3; InitializeAndVerifyExpectedValue(alFiles, c_fileName, CreateRollingFileAppender("3,0,1"), iExpectedValue); } /// /// Verifies that count stays at 0 for the zero backups case /// when counting up /// [Test] public void TestInitializeCountUpZeroBackups() { ArrayList alFiles = MakeTestDataFromString("0,3"); int iExpectedValue = 0; InitializeAndVerifyExpectedValue(alFiles, c_fileName, CreateRollingFileAppender("0,0,1"), iExpectedValue); } /// /// Verifies that count stays at 0 for the zero backups case /// when counting down /// [Test] public void TestInitializeCountDownZeroBackups() { ArrayList alFiles = MakeTestDataFromString("0,3"); int iExpectedValue = 0; InitializeAndVerifyExpectedValue(alFiles, c_fileName, CreateRollingFileAppender("0,0,-1"), iExpectedValue); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed() { ArrayList alFiles = MakeTestDataFromString("4,5,6"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 0); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed2() { ArrayList alFiles = MakeTestDataFromString("1,5,6"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 1); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed3() { ArrayList alFiles = MakeTestDataFromString("2,5,6"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 2); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed4() { ArrayList alFiles = MakeTestDataFromString("3,5,6"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 3); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed5() { ArrayList alFiles = MakeTestDataFromString("1,2,3"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 3); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed6() { ArrayList alFiles = MakeTestDataFromString("1,2"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 2); } /// /// Verifies that count goes to the highest when counting up /// [Test] public void TestInitializeCountDownFixed7() { ArrayList alFiles = MakeTestDataFromString("2,3"); VerifyInitializeDownFixedExpectedValue(alFiles, c_fileName, 3); } private static void InitializeAndVerifyExpectedValue(ArrayList alFiles, string sBaseFile, RollingFileAppender rfa, int iExpectedValue) { InitializeRollBackups(rfa, sBaseFile, alFiles); Assert.AreEqual(iExpectedValue, GetFieldCurSizeRollBackups(rfa)); } /// /// Tests the count down case, with infinite max backups, to see that /// initialization of the rolling file appender results in the expected value /// /// /// /// private static void VerifyInitializeDownInfiniteExpectedValue(ArrayList alFiles, string sBaseFile, int iExpectedValue) { InitializeAndVerifyExpectedValue(alFiles, sBaseFile, CreateRollingFileAppender("-1,0,-1"), iExpectedValue); } /// /// Creates a RollingFileAppender with the desired values, where the /// values are passed as a comma separated string, with 3 parameters, /// m_maxSizeRollBackups, m_curSizeRollBackups, CountDirection /// /// /// private static RollingFileAppender CreateRollingFileAppender(string sParams) { string[] asParams = sParams.Split(','); if (null == asParams || asParams.Length != 3) { throw new ArgumentOutOfRangeException(sParams, sParams, "Must have 3 comma separated params: MaxSizeRollBackups, CurSizeRollBackups, CountDirection"); } RollingFileAppender rfa = new RollingFileAppender(); rfa.RollingStyle = RollingFileAppender.RollingMode.Size; SetFieldMaxSizeRollBackups(rfa, Int32.Parse(asParams[0].Trim())); SetFieldCurSizeRollBackups(rfa, Int32.Parse(asParams[1].Trim())); rfa.CountDirection = Int32.Parse(asParams[2].Trim()); return rfa; } /// /// Verifies that count goes to the highest when counting down /// and infinite backups are selected /// [Test] public void TestInitializeCountDownInfinite() { ArrayList alFiles = MakeTestDataFromString("2,3"); VerifyInitializeDownInfiniteExpectedValue(alFiles, c_fileName, 3); } /// /// Verifies that count goes to the highest when counting down /// and infinite backups are selected /// [Test] public void TestInitializeCountDownInfinite2() { ArrayList alFiles = MakeTestDataFromString("2,3,4,5,6,7,8,9,10"); VerifyInitializeDownInfiniteExpectedValue(alFiles, c_fileName, 10); } /// /// Verifies that count goes to the highest when counting down /// and infinite backups are selected /// [Test] public void TestInitializeCountDownInfinite3() { ArrayList alFiles = MakeTestDataFromString("9,10,3,4,5,7,9,6,1,2,8"); VerifyInitializeDownInfiniteExpectedValue(alFiles, c_fileName, 10); } /// /// Verifies that count goes to the highest when counting up /// and infinite backups are selected /// [Test] public void TestInitializeCountUpInfinite() { ArrayList alFiles = MakeTestDataFromString("2,3"); VerifyInitializeUpInfiniteExpectedValue(alFiles, c_fileName, 3); } /// /// Verifies that count goes to the highest when counting up /// and infinite backups are selected /// [Test] public void TestInitializeCountUpInfinite2() { ArrayList alFiles = MakeTestDataFromString("2,3,4,5,6,7,8,9,10"); VerifyInitializeUpInfiniteExpectedValue(alFiles, c_fileName, 10); } /// /// Verifies that count goes to the highest when counting up /// and infinite backups are selected /// [Test] public void TestInitializeCountUpInfinite3() { ArrayList alFiles = MakeTestDataFromString("9,10,3,4,5,7,9,6,1,2,8"); VerifyInitializeUpInfiniteExpectedValue(alFiles, c_fileName, 10); } /// /// Creates a logger hierarchy, configures a rolling file appender and returns an ILogger /// /// The filename to log to /// The locking model to use. /// The error handler to use. /// A configured ILogger private static ILogger CreateLogger(string filename, FileAppender.LockingModelBase lockModel, IErrorHandler handler) { return CreateLogger(filename, lockModel, handler, 100000, 0); } /// /// Creates a logger hierarchy, configures a rolling file appender and returns an ILogger /// /// The filename to log to /// The locking model to use. /// The error handler to use. /// Maximum file size for roll /// Maximum number of roll backups /// A configured ILogger private static ILogger CreateLogger(string filename, FileAppender.LockingModelBase lockModel, IErrorHandler handler, int maxFileSize, int maxSizeRollBackups) { Repository.Hierarchy.Hierarchy h = (Repository.Hierarchy.Hierarchy)LogManager.CreateRepository("TestRepository"); RollingFileAppender appender = new RollingFileAppender(); appender.File = filename; appender.AppendToFile = false; appender.CountDirection = 0; appender.RollingStyle = RollingFileAppender.RollingMode.Size; appender.MaxFileSize = maxFileSize; appender.Encoding = Encoding.ASCII; appender.ErrorHandler = handler; appender.MaxSizeRollBackups = maxSizeRollBackups; if (lockModel != null) { appender.LockingModel = lockModel; } PatternLayout layout = new PatternLayout(); layout.ConversionPattern = "%m%n"; layout.ActivateOptions(); appender.Layout = layout; appender.ActivateOptions(); h.Root.AddAppender(appender); h.Configured = true; ILogger log = h.GetLogger("Logger"); return log; } /// /// Destroys the logger hierarchy created by /// private static void DestroyLogger() { Repository.Hierarchy.Hierarchy h = (Repository.Hierarchy.Hierarchy)LogManager.GetRepository("TestRepository"); h.ResetConfiguration(); //Replace the repository selector so that we can recreate the hierarchy with the same name if necessary LoggerManager.RepositorySelector = new DefaultRepositorySelector(typeof(log4net.Repository.Hierarchy.Hierarchy)); } private static void AssertFileEquals(string filename, string contents) { StreamReader sr = new StreamReader(filename); string logcont = sr.ReadToEnd(); sr.Close(); Assert.AreEqual(contents, logcont, "Log contents is not what is expected"); File.Delete(filename); } /// /// Verifies that logging a message actually produces output /// [Test] public void TestLogOutput() { String filename = "test.log"; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.ExclusiveLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); AssertFileEquals(filename, "This is a message" + Environment.NewLine + "This is a message 2" + Environment.NewLine); Assert.AreEqual("", sh.Message, "Unexpected error message"); } /// /// Verifies that attempting to log to a locked file fails gracefully /// [Test] public void TestExclusiveLockFails() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.ExclusiveLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); fs.Close(); AssertFileEquals(filename, "Test"); Assert.AreEqual(sh.Message.Substring(0, 30), "Unable to acquire lock on file", "Expecting an error message"); } /// /// Verifies that attempting to log to a locked file recovers if the lock is released /// [Test] public void TestExclusiveLockRecovers() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.ExclusiveLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); fs.Close(); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); AssertFileEquals(filename, "This is a message 2" + Environment.NewLine); Assert.AreEqual("Unable to acquire lock on file", sh.Message.Substring(0, 30), "Expecting an error message"); } /// /// Verifies that attempting to log to a file with ExclusiveLock really locks the file /// [Test] public void TestExclusiveLockLocks() { String filename = "test.log"; bool locked = false; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.ExclusiveLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); try { FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); fs.Close(); } catch(IOException e1) { #if MONO Assert.AreEqual("Sharing violation on path ", e1.Message.Substring(0, 26), "Unexpected exception"); #else Assert.AreEqual("The process cannot access the file ", e1.Message.Substring(0, 35), "Unexpected exception"); #endif locked = true; } log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); Assert.IsTrue(locked, "File was not locked"); #if !MONO || MONO_3_5 || MONO_4_0 // at least on Linux with Mono 2.4 exclusive locking doesn't work as one would expect AssertFileEquals(filename, "This is a message" + Environment.NewLine + "This is a message 2" + Environment.NewLine); #endif Assert.AreEqual("", sh.Message, "Unexpected error message"); } /// /// Verifies that attempting to log to a locked file fails gracefully /// [Test] public void TestMinimalLockFails() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.MinimalLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); fs.Close(); AssertFileEquals(filename, "Test"); Assert.AreEqual("Unable to acquire lock on file", sh.Message.Substring(0, 30), "Expecting an error message"); } /// /// Verifies that attempting to log to a locked file recovers if the lock is released /// [Test] public void TestMinimalLockRecovers() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.MinimalLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); fs.Close(); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); AssertFileEquals(filename, "This is a message 2" + Environment.NewLine); Assert.AreEqual("Unable to acquire lock on file", sh.Message.Substring(0, 30), "Expecting an error message"); } /// /// Verifies that attempting to log to a file with MinimalLock doesn't lock the file /// [Test] public void TestMinimalLockUnlocks() { String filename = "test.log"; bool locked; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.MinimalLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); locked = true; FileStream fs = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test" + Environment.NewLine), 0, 4 + Environment.NewLine.Length); fs.Close(); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); Assert.IsTrue(locked, "File was not locked"); AssertFileEquals(filename, "This is a message" + Environment.NewLine + "Test" + Environment.NewLine + "This is a message 2" + Environment.NewLine); Assert.AreEqual("", sh.Message, "Unexpected error message"); } #if !NETCF /// /// Verifies that attempting to log to a locked file fails gracefully /// [Test] public void TestInterProcessLockFails() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.InterProcessLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); fs.Close(); AssertFileEquals(filename, "Test"); Assert.AreEqual("Unable to acquire lock on file", sh.Message.Substring(0, 30), "Expecting an error message"); } /// /// Verifies that attempting to log to a locked file recovers if the lock is released /// [Test] public void TestInterProcessLockRecovers() { String filename = "test.log"; FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None); fs.Write(Encoding.ASCII.GetBytes("Test"), 0, 4); SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.InterProcessLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); fs.Close(); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); AssertFileEquals(filename, "This is a message 2" + Environment.NewLine); Assert.AreEqual("Unable to acquire lock on file", sh.Message.Substring(0, 30), "Expecting an error message"); } /// /// Verifies that attempting to log to a file with InterProcessLock really locks the file /// [Test] public void TestInterProcessLockUnlocks() { String filename = "test.log"; bool locked; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.InterProcessLock(), sh); log.Log(GetType(), Level.Info, "This is a message", null); locked = true; FileStream fs = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); fs.Write(Encoding.ASCII.GetBytes("Test" + Environment.NewLine), 0, 4 + Environment.NewLine.Length); fs.Close(); log.Log(GetType(), Level.Info, "This is a message 2", null); DestroyLogger(); Assert.IsTrue(locked, "File was not locked"); AssertFileEquals(filename, "This is a message" + Environment.NewLine + "Test" + Environment.NewLine + "This is a message 2" + Environment.NewLine); Assert.AreEqual("", sh.Message, "Unexpected error message"); } /// /// Verifies that rolling file works /// [Test] public void TestInterProcessLockRoll() { String filename = "test.log"; bool locked; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, new FileAppender.InterProcessLock(), sh, 1, 2); Assert.DoesNotThrow(delegate { log.Log(GetType(), Level.Info, "A", null); }); Assert.DoesNotThrow(delegate { log.Log(GetType(), Level.Info, "A", null); }); DestroyLogger(); AssertFileEquals(filename, "A" + Environment.NewLine); AssertFileEquals(filename + ".1", "A" + Environment.NewLine); Assert.IsEmpty(sh.Message); } #endif /// /// Verify that the default LockModel is ExclusiveLock, to maintain backwards compatibility with previous behaviour /// [Test] public void TestDefaultLockingModel() { String filename = "test.log"; SilentErrorHandler sh = new SilentErrorHandler(); ILogger log = CreateLogger(filename, null, sh); IAppender[] appenders = log.Repository.GetAppenders(); Assert.AreEqual(1, appenders.Length, "The wrong number of appenders are configured"); RollingFileAppender rfa = (RollingFileAppender)(appenders[0]); Assert.AreEqual(typeof(log4net.Appender.FileAppender.ExclusiveLock), rfa.LockingModel.GetType(), "The LockingModel is of an unexpected type"); DestroyLogger(); } /// /// Tests the count up case, with infinite max backups , to see that /// initialization of the rolling file appender results in the expected value /// /// /// /// private static void VerifyInitializeUpInfiniteExpectedValue(ArrayList alFiles, string sBaseFile, int iExpectedValue) { InitializeAndVerifyExpectedValue(alFiles, sBaseFile, CreateRollingFileAppender("-1,0,1"), iExpectedValue); } /// /// Tests the count down case, with max backups limited to 3, to see that /// initialization of the rolling file appender results in the expected value /// /// /// /// private static void VerifyInitializeDownFixedExpectedValue(ArrayList alFiles, string sBaseFile, int iExpectedValue) { InitializeAndVerifyExpectedValue(alFiles, sBaseFile, CreateRollingFileAppender("3,0,-1"), iExpectedValue); } /// /// Turns a string of comma separated numbers into a collection of filenames /// generated from the numbers. /// /// Defaults to filename in _fileName variable. /// /// /// Comma separated list of numbers for counted file names /// private static ArrayList MakeTestDataFromString(string sFileNumbers) { return MakeTestDataFromString(c_fileName, sFileNumbers); } /// /// Turns a string of comma separated numbers into a collection of filenames /// generated from the numbers /// /// Uses the input filename. /// /// Name of file to combine with numbers when generating counted file names /// Comma separated list of numbers for counted file names /// private static ArrayList MakeTestDataFromString(string sFileName, string sFileNumbers) { ArrayList alFiles = new ArrayList(); string[] sNumbers = sFileNumbers.Split(','); foreach(string sNumber in sNumbers) { Int32 iValue = Int32.Parse(sNumber.Trim()); alFiles.Add(MakeFileName(sFileName, iValue)); } return alFiles; } /// /// Tests that the current backup index is correctly detected /// for a file with no extension /// [Test] public void TestInitializeRollBackups2() { VerifyInitializeRollBackupsFromBaseFile("LogFile"); } /// /// Tests that the current backup index is correctly detected /// for a file with a .log extension /// [Test] public void TestInitializeRollBackups3() { VerifyInitializeRollBackupsFromBaseFile("LogFile.log"); } /// /// Makes sure that the initialization can detect the backup /// number correctly. /// /// /// public void VerifyInitializeRollBackups(int iBackups, int iMaxSizeRollBackups) { string sBaseFile = "LogFile.log"; ArrayList arrFiles = new ArrayList(); arrFiles.Add("junk1"); for(int i = 0; i < iBackups; i++) { arrFiles.Add(MakeFileName(sBaseFile, i)); } RollingFileAppender rfa = new RollingFileAppender(); rfa.RollingStyle = RollingFileAppender.RollingMode.Size; SetFieldMaxSizeRollBackups(rfa, iMaxSizeRollBackups); SetFieldCurSizeRollBackups(rfa, 0); InitializeRollBackups(rfa, sBaseFile, arrFiles); // iBackups / Meaning // 0 = none // 1 = file.log // 2 = file.log.1 // 3 = file.log.2 if (0 == iBackups || 1 == iBackups) { Assert.AreEqual(0, GetFieldCurSizeRollBackups(rfa)); } else { Assert.AreEqual(Math.Min(iBackups - 1, iMaxSizeRollBackups), GetFieldCurSizeRollBackups(rfa)); } } /// /// Tests that the current backup index is correctly detected, /// and gets no bigger than the max backups setting /// [Test] public void TestInitializeRollBackups4() { const int iMaxRollBackups = 5; VerifyInitializeRollBackups(0, iMaxRollBackups); VerifyInitializeRollBackups(1, iMaxRollBackups); VerifyInitializeRollBackups(2, iMaxRollBackups); VerifyInitializeRollBackups(3, iMaxRollBackups); VerifyInitializeRollBackups(4, iMaxRollBackups); VerifyInitializeRollBackups(5, iMaxRollBackups); VerifyInitializeRollBackups(6, iMaxRollBackups); // Final we cap out at the max value VerifyInitializeRollBackups(7, iMaxRollBackups); VerifyInitializeRollBackups(8, iMaxRollBackups); } /// /// /// [Test, Ignore("Not Implemented: Want to test counted files limited up, to see that others are ?? ignored? deleted?")] public void TestInitialization3() { } /// /// /// [Test, Ignore("Not Implemented: Want to test counted files limited down, to see that others are ?? ignored? deleted?")] public void TestInitialization4() { } /// /// /// [Test, Ignore("Not Implemented: Want to test dated files with a limit, to see that others are ?? ignored? deleted?")] public void TestInitialization5() { } /// /// /// [Test, Ignore("Not Implemented: Want to test dated files with no limit, to see that others are ?? ignored? deleted?")] public void TestInitialization6() { } /// /// /// [Test, Ignore("Not Implemented: Want to test dated files with mixed dates existing, to see that other dates do not matter")] public void TestInitialization7() { } // // Helper functions to dig into the appender // private static ArrayList GetExistingFiles(string baseFilePath) { RollingFileAppender appender = new RollingFileAppender(); appender.SecurityContext = NullSecurityContext.Instance; return (ArrayList)Utils.InvokeMethod(appender, "GetExistingFiles", baseFilePath); } private static void InitializeRollBackups(RollingFileAppender appender, string baseFile, ArrayList arrayFiles) { Utils.InvokeMethod(appender, "InitializeRollBackups", baseFile, arrayFiles); } private static int GetFieldCurSizeRollBackups(RollingFileAppender appender) { return (int)Utils.GetField(appender, "m_curSizeRollBackups"); } private static void SetFieldCurSizeRollBackups(RollingFileAppender appender, int val) { Utils.SetField(appender, "m_curSizeRollBackups", val); } private static void SetFieldMaxSizeRollBackups(RollingFileAppender appender, int val) { Utils.SetField(appender, "m_maxSizeRollBackups", val); } private static string GetTestMessage() { switch (Environment.NewLine.Length) { case 2: return c_testMessage98Chars; case 1: return c_testMessage99Chars; default: throw new Exception("Unexpected Environment.NewLine.Length"); } } } [TestFixture] public class RollingFileAppenderSubClassTest : RollingFileAppender { [Test] public void TestComputeCheckPeriod() { RollingFileAppender rfa = new RollingFileAppender(); Assert.AreEqual(RollPoint.TopOfMinute, InvokeComputeCheckPeriod(rfa, ".yyyy-MM-dd HH:mm"), "TopOfMinute pattern"); Assert.AreEqual(RollPoint.TopOfHour, InvokeComputeCheckPeriod(rfa, ".yyyy-MM-dd HH"), "TopOfHour pattern"); Assert.AreEqual(RollPoint.HalfDay, InvokeComputeCheckPeriod(rfa, ".yyyy-MM-dd tt"), "HalfDay pattern"); Assert.AreEqual(RollPoint.TopOfDay, InvokeComputeCheckPeriod(rfa, ".yyyy-MM-dd"), "TopOfDay pattern"); Assert.AreEqual(RollPoint.TopOfMonth, InvokeComputeCheckPeriod(rfa, ".yyyy-MM"), "TopOfMonth pattern"); // Test invalid roll point Assert.AreEqual(RollPoint.InvalidRollPoint, InvokeComputeCheckPeriod(rfa, "..."), "TopOfMonth pattern"); } private static RollPoint InvokeComputeCheckPeriod(RollingFileAppender rollingFileAppender, string datePattern) { return (RollPoint)Utils.InvokeMethod(rollingFileAppender, "ComputeCheckPeriod", datePattern); } } }