Merge branch 'develop' into feature/RollingFileAppender-NG
diff --git a/src/Appender/Rolling/CronRollingCondition.cs b/src/Appender/Rolling/CronRollingCondition.cs
new file mode 100644
index 0000000..f355648
--- /dev/null
+++ b/src/Appender/Rolling/CronRollingCondition.cs
@@ -0,0 +1,230 @@
+#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.IO;
+
+namespace log4net.Appender.Rolling
+{
+    /// <summary>
+    /// A implementation of the <see cref="IRollingCondition"/> interface that rolls
+    /// the file cronologically.
+    /// </summary>
+    /// <author>Dominik Psenner</author>
+    public class CronRollingCondition : IRollingCondition
+    {
+        #region Public Instance Constructors
+
+        public CronRollingCondition()
+            : this("*", "*", "*", "*", "*")
+        {
+        }
+
+        public CronRollingCondition(string dow, string month, string day, string hour, string minute)
+        {
+            Dow = TryParse(dow);
+            Month = TryParse(month);
+            Day = TryParse(day);
+            Hour = TryParse(hour);
+            Minute = TryParse(minute);
+        }
+
+        #endregion
+
+        #region Protected Instance Properties
+
+        protected Tuple<int?, MatchType> Dow { get; private set; }
+        protected Tuple<int?, MatchType> Month { get; private set; }
+        protected Tuple<int?, MatchType> Day { get; private set; }
+        protected Tuple<int?, MatchType> Hour { get; private set; }
+        protected Tuple<int?, MatchType> Minute { get; private set; }
+
+        #endregion
+
+        #region Private Instance Properties
+
+        private ulong last_rolled = 0;
+
+        #endregion
+
+        #region Protected Inner Classes
+
+        protected enum MatchType
+        {
+            Nothing,
+            Exact,
+            Remainder,
+        }
+
+        #endregion
+
+        #region Protected Static Methods
+
+        /// <summary>
+        /// This method parses a string to match any of these:
+        /// i
+        /// *
+        /// */i
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        static protected Tuple<int?, MatchType> TryParse(string input)
+        {
+            // trim input and strip empty spaces
+            string inputParsed = input.Trim().Replace(" ", "").Replace("\t", "");
+
+            // match a remainder: */c
+            if (inputParsed.StartsWith("*/") || inputParsed.StartsWith(@"*\"))
+            {
+                // strip first two chars
+                inputParsed = inputParsed.Substring(2);
+                // parse the remainder
+                int i = -1;
+                if (int.TryParse(inputParsed, out i))
+                {
+                    return Tuple.Create<int?, MatchType>(i, MatchType.Remainder);
+                }
+            }
+            else if (inputParsed.StartsWith("*")) // match anything: *
+            {
+                return Tuple.Create<int?, MatchType>(null, MatchType.Nothing);
+            }
+            else // match one specific numer: i
+            {
+                int i = -1;
+                if (int.TryParse(inputParsed, out i))
+                {
+                    return Tuple.Create<int?, MatchType>(i, MatchType.Exact);
+                }
+            }
+
+            // throw exception by default
+            throw new FormatException(string.Format("The input string '{0}' could not be parsed to a valid format.", input));
+        }
+
+        #endregion
+
+        #region Implementation of IRollingCondition
+
+        public bool IsMet(string file)
+        {
+            return IsMet(DateTime.Now);
+        }
+
+        private static ulong GetUniqueIndex(DateTime now)
+        {
+            ulong result = (ulong)now.DayOfWeek;
+            result <<= 3;
+            result += (ulong)now.Month;
+            result <<= 4;
+            result += (ulong)now.Day;
+            result <<= 5;
+            result += (ulong)now.Hour;
+            result <<= 5;
+            result += (ulong)now.Minute;
+            return result;
+        }
+
+        #endregion
+
+        #region Public Methods
+
+        public bool IsMet(DateTime now)
+        {
+            Console.WriteLine("test {0}", now);
+            // check only every minute
+            // we can skip the check as we checked this minute already 
+            // and if we don't we may run into the situation to roll a file twice
+            if (GetUniqueIndex(now) == last_rolled)
+            {
+                Console.WriteLine("  skipped");
+                return false;
+            }
+            if (!IsMet(Dow, (int)now.DayOfWeek))
+            {
+                return false;
+            }
+            if (!IsMet(Month, now.Month))
+            {
+                return false;
+            }
+            if (!IsMet(Day, now.Day))
+            {
+                return false;
+            }
+            if (!IsMet(Hour, now.Hour))
+            {
+                return false;
+            }
+            if (!IsMet(Minute, now.Minute))
+            {
+                return false;
+            }
+
+            last_rolled = GetUniqueIndex(now);
+            return true;
+        }
+
+        #endregion
+
+        #region Private Methods
+
+        private bool IsMet(Tuple<int?, MatchType> match, int item)
+        {
+            switch (match.Item2)
+            {
+                case MatchType.Exact:
+                    Console.WriteLine("  {0} != {1} == {2}", match.Item1.Value, item, match.Item1.Value != item);
+                    if (match.Item1.Value != item)
+                    {
+                        return false;
+                    }
+                    break;
+                case MatchType.Remainder:
+                    // special case: */0, the division through 0 is undefined; this match should pass
+                    if (match.Item1.Value == 0)
+                    {
+                        Console.WriteLine("{0} % 0 == 0", item);
+                        return false;
+                    }
+                    else
+                    {
+                        Console.WriteLine("  {0} % {1} == {2}", item, match.Item1.Value, item % match.Item1.Value);
+                        if (item % match.Item1.Value != 0)
+                        {
+                            return false;
+                        }
+                    }
+                    break;
+            }
+            return true;
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Converts the given rolling condition to a nicely formatted string representation.
+        /// </summary>
+        /// <returns></returns>
+        public override string ToString()
+        {
+            return string.Format("{0} {1} {2} {3} {4}", Dow, Month, Day, Hour, Minute);
+        }
+    }
+}
diff --git a/src/Appender/Rolling/IRollingCondition.cs b/src/Appender/Rolling/IRollingCondition.cs
new file mode 100644
index 0000000..e7777b6
--- /dev/null
+++ b/src/Appender/Rolling/IRollingCondition.cs
@@ -0,0 +1,38 @@
+#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;
+
+namespace log4net.Appender.Rolling
+{
+    /// <summary>
+    /// The interface definition of a rolling condition.
+    /// </summary>
+    /// <author>Dominik Psenner</author>
+    public interface IRollingCondition
+    {
+        /// <summary>
+        /// This method should implement all checks needed to determine if a file
+        /// can be rolled based on the conditions it implies to the file.
+        /// </summary>
+        /// <param name="file">the file to be rolled</param>
+        /// <returns>true when the file has met all conditions to be rolled</returns>
+        bool IsMet(string file);
+    }
+}
diff --git a/src/Appender/Rolling/IRollingStrategy.cs b/src/Appender/Rolling/IRollingStrategy.cs
new file mode 100644
index 0000000..94ad6d2
--- /dev/null
+++ b/src/Appender/Rolling/IRollingStrategy.cs
@@ -0,0 +1,36 @@
+#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;
+
+namespace log4net.Appender.Rolling
+{
+    /// <summary>
+    /// An interface that can implement a strategy to roll a file.
+    /// </summary>
+    /// <author>Dominik Psenner</author>
+    public interface IRollingStrategy
+    {
+        /// <summary>
+        /// This method should implement all the rolling operations.
+        /// </summary>
+        /// <param name="file">the file to be rolled</param>
+        void Roll(string file);
+    }
+}
diff --git a/src/Appender/Rolling/IndexRollingStrategy.cs b/src/Appender/Rolling/IndexRollingStrategy.cs
new file mode 100644
index 0000000..ad58b40
--- /dev/null
+++ b/src/Appender/Rolling/IndexRollingStrategy.cs
@@ -0,0 +1,77 @@
+#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.IO;
+
+namespace log4net.Appender.Rolling
+{
+    /// <summary>
+    /// This is a simple rolling strategy implementation that rolls a file by
+    /// appending the index 0. If a file exists that is named exactly as that
+    /// that file will be renamed to index 1 until reaching the maximum index.
+    /// The last file will be removed.
+    /// </summary>
+    /// <author>Dominik Psenner</author>
+    public class IndexRollingStrategy : IRollingStrategy
+    {
+        #region Implementation of IRollingStrategy
+
+        /// <summary>
+        /// This method rolls a file with backup indexes between
+        /// [0..10].
+        /// </summary>
+        /// <param name="file"></param>
+        public void Roll(string file)
+        {
+            DoRoll(file, file, 0, 10);
+        }
+
+        #endregion
+
+        #region Private Methods
+
+        private void DoRoll(string baseFilename, string currentFilename, int currentIndex, int maxIndex)
+        {
+            if (currentIndex > maxIndex)
+            {
+                if (File.Exists(currentFilename))
+                {
+                    File.Delete(currentFilename);
+                }
+                return;
+            }
+            if (!File.Exists(currentFilename))
+            {
+                return;
+            }
+
+            // determine next filename
+            string nextFilename = string.Format("{0}.{1}", baseFilename, currentIndex);
+
+            // iterate the process until we meet the end
+            DoRoll(baseFilename, nextFilename, currentIndex + 1, maxIndex);
+
+            // rename this file now that there's free room after us
+            File.Move(currentFilename, nextFilename);
+        }
+
+        #endregion
+    }
+}
diff --git a/src/Appender/RollingFileAppenderNG.cs b/src/Appender/RollingFileAppenderNG.cs
new file mode 100644
index 0000000..4b79983
--- /dev/null
+++ b/src/Appender/RollingFileAppenderNG.cs
@@ -0,0 +1,152 @@
+#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.Globalization;
+using System.IO;
+
+using log4net.Util;
+using log4net.Core;
+using log4net.Appender.Rolling;
+
+namespace log4net.Appender
+{
+    /// <summary>
+    /// Appender that rolls log files based on size or date or both.
+    /// </summary>
+    /// <author>Dominik Psenner</author>
+    public class RollingFileAppenderNG : FileAppender
+    {
+        #region Public Instance Constructors
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RollingFileAppenderNG" /> class.
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// Default constructor.
+        /// </para>
+        /// </remarks>
+        public RollingFileAppenderNG()
+        {
+            // for now set up the cron rolling condition and the index rolling strategy by default
+            RollingCondition = new CronRollingCondition("*", "*", "*", "*", "*");
+            RollingStrategy = new IndexRollingStrategy();
+        }
+
+        #endregion Public Instance Constructors
+
+        #region Public Instance Properties
+
+        /// <summary>
+        /// Gets the strategy to decide whether or not a file should be rolled over.
+        /// </summary>
+        public IRollingCondition RollingCondition
+        {
+            get;
+            private set;
+        }
+
+        /// <summary>
+        /// Gets the strategy to roll a file.
+        /// </summary>
+        public IRollingStrategy RollingStrategy
+        {
+            get;
+            private set;
+        }
+
+        #endregion Public Instance Properties
+
+        #region Override implementation of FileAppender 
+
+        /// <summary>
+        /// Sets the quiet writer being used.
+        /// </summary>
+        /// <remarks>
+        /// This method can be overridden by sub classes.
+        /// </remarks>
+        /// <param name="writer">the writer to set</param>
+        override protected void SetQWForFiles(TextWriter writer)
+        {
+            QuietWriter = new CountingQuietTextWriter(writer, ErrorHandler);
+        }
+
+        /// <summary>
+        /// Write out a logging event.
+        /// </summary>
+        /// <param name="loggingEvent">the event to write to file.</param>
+        /// <remarks>
+        /// <para>
+        /// Handles append time behavior for RollingFileAppenderNG.
+        /// </para>
+        /// </remarks>
+        override protected void Append(LoggingEvent loggingEvent)
+        {
+            RollFileTrigger();
+            base.Append(loggingEvent);
+        }
+
+        /// <summary>
+        /// Write out an array of logging events.
+        /// </summary>
+        /// <param name="loggingEvents">the events to write to file.</param>
+        /// <remarks>
+        /// <para>
+        /// Handles append time behavior for RollingFileAppenderNG.
+        /// </para>
+        /// </remarks>
+        override protected void Append(LoggingEvent[] loggingEvents)
+        {
+            RollFileTrigger();
+            base.Append(loggingEvents);
+        }
+
+        /// <summary>
+        /// Performs any required rolling before outputting the next event
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// Handles append time behavior for RollingFileAppenderNG. It checks first
+        /// whether the conditions to roll the file are met and if so asks the roll
+        /// file strategy to do the roll operation.
+        /// </para>
+        /// </remarks>
+        protected void RollFileTrigger()
+        {
+            if (RollingCondition == null)
+            {
+                // TODO throw exception
+            }
+            if (RollingStrategy == null)
+            {
+                // TODO throw exception
+            }
+
+            // check if the rolling conditions are met
+            if (RollingCondition.IsMet(File))
+            {
+                // let the strategy do all the required rolling
+                RollingStrategy.Roll(File);
+            }
+        }
+        #endregion
+    }
+}
diff --git a/src/log4net.csproj b/src/log4net.csproj
index 05a7062..a7ee576 100644
--- a/src/log4net.csproj
+++ b/src/log4net.csproj
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!--
 
  Licensed to the Apache Software Foundation (ASF) under one
@@ -191,6 +191,11 @@
     <Compile Include="Appender\RollingFileAppender.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Appender\RollingFileAppenderNG.cs" />
+    <Compile Include="Appender\Rolling\CronRollingCondition.cs" />
+    <Compile Include="Appender\Rolling\IndexRollingStrategy.cs" />
+    <Compile Include="Appender\Rolling\IRollingCondition.cs" />
+    <Compile Include="Appender\Rolling\IRollingStrategy.cs" />
     <Compile Include="Appender\SmtpAppender.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -805,4 +810,4 @@
     <PostBuildEvent>
     </PostBuildEvent>
   </PropertyGroup>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/log4net.sln b/src/log4net.sln
index 3d422e3..c3621c5 100644
--- a/src/log4net.sln
+++ b/src/log4net.sln
@@ -20,6 +20,9 @@
 # under the License.
 #
 #
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "log4net", "log4net.csproj", "{181FE707-E161-4722-9F38-6AAAB6FAA106}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "log4net.Tests", "..\tests\src\log4net.Tests.csproj", "{B0530F10-0238-49A9-93B0-8EF412E90BCF}"
diff --git a/tests/src/Appender/Rolling/CronRollingConditionTest.cs b/tests/src/Appender/Rolling/CronRollingConditionTest.cs
new file mode 100644
index 0000000..8a7f28a
--- /dev/null
+++ b/tests/src/Appender/Rolling/CronRollingConditionTest.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using log4net.Appender.Rolling;
+
+namespace log4net.Tests.Appender.Rolling
+{
+    [TestFixture]
+    public class CronRollingConditionTest
+    {
+        [Test]
+        public void IsMetTest()
+        {
+            Tuple<CronRollingCondition, Tuple<DateTime, bool>[]>[] tests = new Tuple<CronRollingCondition, Tuple<DateTime, bool>[]>[]{
+                Tuple.Create(new CronRollingCondition("*", "*", "*", "*", "5"), new Tuple<DateTime, bool>[]{
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 5, 0), true),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 5, 1), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 6, 0), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 10, 0), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 13, 5, 1), true),
+                    Tuple.Create(new DateTime(2009, 10, 10, 14, 5, 1), true),
+                }),
+                Tuple.Create(new CronRollingCondition("*", "*", "*", "*", "*/3"), new Tuple<DateTime, bool>[]{
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 0, 0), true),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 0, 1), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 0, 59), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 3, 0), true),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 3, 59), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 4, 1), false),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 6, 33), true),
+                    Tuple.Create(new DateTime(2009, 10, 10, 12, 10, 0), false),
+                }),
+            };
+            foreach (Tuple<CronRollingCondition, Tuple<DateTime, bool>[]> test in tests)
+            {
+                foreach (Tuple<DateTime, bool> testCheck in test.Item2)
+                {
+                    Assert.AreEqual(test.Item1.IsMet(testCheck.Item1), testCheck.Item2, string.Format("failed for {0} with condition {1}", testCheck.Item1, test.Item1));
+                }
+            }
+        }
+    }
+}
diff --git a/tests/src/Appender/Rolling/IndexRollingStrategyTest.cs b/tests/src/Appender/Rolling/IndexRollingStrategyTest.cs
new file mode 100644
index 0000000..b66f7fc
--- /dev/null
+++ b/tests/src/Appender/Rolling/IndexRollingStrategyTest.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using log4net.Appender.Rolling;
+using System.IO;
+
+namespace log4net.Tests.Appender.Rolling
+{
+    [TestFixture]
+    public class IndexRollingStrategyTest
+    {
+        [Test]
+        public void RollTest()
+        {
+            IndexRollingStrategy strategy = new IndexRollingStrategy();
+            List<string> tmpFiles = new List<string>();
+            try
+            {
+                // create a dummy temp file
+                string filename = "logfile.log";
+                // add the file
+                tmpFiles.Add(filename);
+                for (int i = 0; i <= 10; i++)
+                {
+                    Console.WriteLine("Now at rolling iteration {0}", i);
+                    // roll the file once
+                    strategy.Roll(filename);
+                    // create filename again
+                    using (File.Create(filename))
+                    {
+                        // we don't need the filestream anymore
+                    }
+                    tmpFiles.Add(filename + "." + i);
+                    // test if there are rolled files for [0..i]
+                    for (int j = 0; j < i; j++)
+                    {
+                        if (!File.Exists(filename + "." + j))
+                        {
+                            // fail
+                            Assert.Fail("The file '{0}' was not rolled correctly at iteration i={1}; j={2}", filename, i, j);
+                        }
+                    }
+                }
+
+                Console.WriteLine("Now at rolling iteration {0}, this should not create another file", 11);
+                // roll the file once
+                strategy.Roll(filename);
+                // test if there are rolled files for [0..i]
+                for (int j = 0; j <= 10; j++)
+                {
+                    if (!File.Exists(filename + "." + j))
+                    {
+                        // fail
+                        Assert.Fail("The file '{0}' was not rolled correctly at iteration j={2}", filename, j);
+                    }
+                }
+                if (File.Exists(filename + ".11"))
+                {
+                    Assert.Fail("The file '{0}' should not have been rolled further iteration 10", filename);
+                }
+
+            }
+            catch (Exception e)
+            {
+                Assert.Fail("Exception: {0}", e);
+            }
+            finally
+            {
+                // cleanup
+                while (tmpFiles.Count > 0)
+                {
+                    if (File.Exists(tmpFiles[0]))
+                    {
+                        File.Delete(tmpFiles[0]);
+                    }
+                    tmpFiles.RemoveAt(0);
+                }
+            }
+        }
+    }
+}
diff --git a/tests/src/log4net.Tests.csproj b/tests/src/log4net.Tests.csproj
index f1768cc..01f904f 100644
--- a/tests/src/log4net.Tests.csproj
+++ b/tests/src/log4net.Tests.csproj
@@ -159,6 +159,8 @@
     <Compile Include="Appender\RemotingAppenderTest.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="Appender\Rolling\CronRollingConditionTest.cs" />
+    <Compile Include="Appender\Rolling\IndexRollingStrategyTest.cs" />
     <Compile Include="Appender\SmtpPickupDirAppenderTest.cs">
       <SubType>Code</SubType>
     </Compile>