using Falcon.SugarApi.TimedBackgroundTask; using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; namespace Falcon.SugarApi.Test { [TestClass()] public class CronExpressionTests { [TestMethod] public void CronExpressionTest() { var now20250213102512 = new DateTime(2025,2,13,10,25,12); var cron = new CronExpression("0 30 3 * * 1-5"); // 工作日凌晨3:30:00 var nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250214033000"); cron = new CronExpression("");//下一秒触发 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250213102513"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250213102514",nextTime.ToString()); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250213102515",nextTime.ToString()); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250213102516",nextTime.ToString()); cron = new CronExpression("10");//每分钟10秒时触发 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250213102610"); cron = new CronExpression("10 25");//每个小时的25分10秒触发 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250213112510"); cron = new CronExpression("10 25");//每个小时的25分10秒触发 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250213112510"); cron = new CronExpression("10/10 25");//每个小时的25分10秒触发,只有每10秒触发一次 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250213102520"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250213102530"); cron = new CronExpression("0 0 3-5");//每天凌晨3点到5点每小时触发一次 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250214030000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214040000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214050000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250215030000"); cron = new CronExpression("0 0 3-5 * * 1-5");//每个工作日凌晨3点到5点每小时触发一次 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250214030000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214040000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214050000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250217030000",DateToString(nextTime)); cron = new CronExpression("0 0/30 3-5 * * 1-5");//每个工作日凌晨3点到5点每小时触发两次,平均每半小时一次 nextTime = cron.GetNextOccurrence(now20250213102512); Assert.IsTrue(DateToString(nextTime) == "20250214030000",nextTime.ToString()); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214033000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214040000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214043000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214050000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250214053000"); nextTime = cron.GetNextOccurrence(nextTime); Assert.IsTrue(DateToString(nextTime) == "20250217030000"); } private string? DateToString(DateTime? date) => date?.ToString("yyyyMMddHHmmss"); //----------------------------- // 基础语法解析测试 //----------------------------- [TestMethod] public void TestBasicWildcard() { var cron = new CronExpression("* * * * * * *"); Assert.AreEqual(60,cron.Seconds.Count,"秒字段应包含0-59"); Assert.AreEqual(12,cron.Months.Count,"月字段应包含1-12"); } [TestMethod] public void TestFixedValue() { var cron = new CronExpression("5 10 3 15 6 2 2026"); Assert.IsTrue(cron.Seconds.Contains(5)); Assert.IsTrue(cron.Minutes.Contains(10)); Assert.IsTrue(cron.Hours.Contains(3)); Assert.IsTrue(cron.DaysOfMonth.Contains(15)); Assert.IsTrue(cron.Months.Contains(6)); Assert.IsTrue(cron.DaysOfWeek.Contains(2)); Assert.IsTrue(cron.Years.Contains(2026)); } //----------------------------- // 复杂语法测试 //----------------------------- [TestMethod] public void TestRangeAndStep() { //var cron = new CronExpression("0-30/15 10,20,30 * 1-5 */3 * *"); //CollectionAssert.AreEqual(new[] { 0,15,30 },cron.Seconds); //CollectionAssert.AreEqual(new[] { 10,20,30 },cron.Minutes); //CollectionAssert.AreEqual(new[] { 1,2,3,4,5 },cron.DaysOfMonth); //CollectionAssert.AreEqual(new[] { 1,4,7,10 },cron.Months); } //----------------------------- // 字段省略测试 //----------------------------- [TestMethod] public void TestPartialExpression() { var cron = new CronExpression("30 15 4"); Assert.AreEqual(1,cron.Seconds.Count); Assert.AreEqual(1,cron.Minutes.Count); Assert.AreEqual(1,cron.Hours.Count); Assert.AreEqual(31,cron.DaysOfMonth.Count); // 默认1-31 Assert.AreEqual(12,cron.Months.Count); // 默认1-12 } //----------------------------- // 边界值测试 //----------------------------- [TestMethod] public void TestBoundaryValues() { // 最大最小值测试 var cron = new CronExpression("59 59 23 31 12 6 2026"); Assert.IsTrue(cron.Seconds.Contains(59)); Assert.IsTrue(cron.DaysOfMonth.Contains(31)); Assert.IsTrue(cron.Months.Contains(12)); } //----------------------------- // 错误处理测试 //----------------------------- [TestMethod] public void TestInvalidCharacter() { try { var _ = new CronExpression("5a * * * * *"); } catch(CronAdjustNullException) { } catch(CronException) { } } [TestMethod] public void TestOutOfRangeValue() { try { var _ = new CronExpression("70 * * * * *"); } catch(CronAdjustNullException) { } catch(CronException) { } } //----------------------------- // 下次执行时间计算测试 //----------------------------- [TestMethod] public void TestNextOccurrence_Basic() { var baseTime = new DateTime(2025,10,1,12,30,15); var cron = new CronExpression("30 * * * * * *"); // 下一个30秒 var expected = new DateTime(2025,10,1,12,30,30); var actual = cron.GetNextOccurrence(baseTime); Assert.AreEqual(expected,actual); } [TestMethod] public void TestNextOccurrence_MonthRollover() { var baseTime = new DateTime(2024,12,31,23,59,59); var cron = new CronExpression("* * * 1 * * *"); // 每月1号 var expected = new DateTime(2025,1,1,0,0,0); var actual = cron.GetNextOccurrence(baseTime); Assert.AreEqual(expected,actual); } //----------------------------- // 特殊日期处理测试 //----------------------------- [TestMethod] public void TestFebruaryHandling() { // 测试闰年2月29日 var cron = new CronExpression("0 0 0 29 2 * 2028"); Assert.IsTrue(cron.DaysOfMonth.Contains(29)); var nextTime = cron.GetNextOccurrence(new DateTime(2028,2,28)); Assert.AreEqual(new DateTime(2028,2,29),nextTime.Date); } //----------------------------- // 日期与星期冲突测试 //----------------------------- [TestMethod] public void TestDayOfWeekConflict() { // 每月15号且为星期一(2023-10-15是星期日) var cron = new CronExpression("* * * 15 * 1 *"); var baseTime = new DateTime(2023,10,1); var nextTime = cron.GetNextOccurrence(baseTime); Assert.AreEqual(2025,nextTime.Year,nextTime.ToString()); Assert.AreEqual(9,nextTime.Month,nextTime.ToString()); Assert.AreEqual(15,nextTime.Day,nextTime.ToString()); Assert.AreEqual(DayOfWeek.Monday,nextTime.DayOfWeek); } } }