/// <summary>
		/// 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
		/// </summary>
		/// <param name="rollingStats"></param>
		/// <param name="currentNext"></param>
		/// <returns></returns>
		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;
			}
		}
		/// <summary>
		/// 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.
		/// </summary>
		/// <param name="sTestMessage">A message to log repeatedly</param>
		/// <param name="sBackupInfo">Filename groups used to indicate backup file name progression
		/// that results after each message is logged</param>
		/// <param name="iMessagesToLog">How many times the test message will be repeatedly logged</param>
		/// <param name="evaluator">Function that can turn a number into a filename</param>
		/// <returns></returns>
		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;
		}
		/// <summary>
		/// 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.
		/// </summary>
		/// <param name="existing"></param>
		/// <param name="final"></param>
		/// <returns></returns>
		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;
		}
		/// <summary>
		/// Generates the pre and post condition arrays from an array of backup files and the
		/// current file / next file.
		/// </summary>
		/// <param name="sBackupFiles"></param>
		/// <param name="preCondition"></param>
		/// <param name="current"></param>
		/// <param name="currentNext"></param>
		/// <param name="rollingStats"></param>
		/// <returns></returns>
		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);
		}
		/// <summary>
		/// Checks that all the expected files exist, and only the expected files.  Also
		/// verifies the length of all files against the expected length
		/// </summary>
		/// <param name="sBaseFileName"></param>
		/// <param name="fileEntries"></param>
		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);
		}
		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);
		}
			/// <summary>
			/// Constructor, taking all required parameters
			/// </summary>
			/// <param name="preLogFileEntries"></param>
			/// <param name="postLogFileEntries"></param>
			public RollConditions(RollFileEntry[] preLogFileEntries, RollFileEntry[] postLogFileEntries)
			{
				m_preLogFileEntries = preLogFileEntries;
				m_postLogFileEntries = postLogFileEntries;
			}
		private void DumpFileEntry( RollFileEntry entry )
		{
			System.Diagnostics.Debug.WriteLine( "\tfile   name: " + entry.FileName );
			System.Diagnostics.Debug.WriteLine( "\tfile length: " + entry.FileLength );
		}
			private void VerifyExistenceAndRemoveFromList( ArrayList alExisting, string sFileName, FileInfo file, RollFileEntry entry )
			{
				Assertion.Assert( "filename " + sFileName + " not found in test directory", alExisting.Contains( sFileName ) );
				Assertion.AssertEquals( "file length mismatch", entry.FileLength, file.Length );
				// Remove this file from the list
				alExisting.Remove( sFileName );
			}