2014-10-27 64 views
1

我想用反应式扩展(RX)和NCrontab创建可观察序列。该顺序与Observable.Timer()之类的不同之处在于期限和到期时间不固定。在阅读this article之后,似乎Observable.Generate()就是要走的路。我正在考虑两种变体:一种在边界内运行,另一种永远运行。这些实现是否有意义?Cron Observable Sequence

public static IObservable<DateTime> Cron(string cron) 
{ 
    var schedule = CrontabSchedule.Parse(cron); 
    return Observable.Generate(DateTime.Now, d=>true, d => DateTime.Now, d => d, 
     d => new DateTimeOffset(schedule.GetNextOccurrence(d))); 
} 

public static IObservable<DateTime> Cron(string cron, DateTime start, DateTime end) 
{ 
    var schedule = CrontabSchedule.Parse(cron); 
    return Observable.Generate(start, d => d < end, d => DateTime.Now, d => d, 
     d => new DateTimeOffset(schedule.GetNextOccurrence(d))); 
} 

更新:这似乎工作经验,但是我加一个重载需要一个IScheduler似乎并不能得到序列中的单元测试来触发。我是否使用TestScheduler错误或者是否存在函数实现的问题?

public static IObservable<int> Cron(string cron, IScheduler scheduler) 
{ 
    var schedule = CrontabSchedule.Parse(cron); 
    return Observable.Generate(0, d => true, d => d + 1, d => d, 
     d => new DateTimeOffset(schedule.GetNextOccurrence(scheduler.Now.DateTime)), scheduler); 
} 

[TestClass] 
public class EngineTests 
{ 
    [TestMethod] 
    public void TestCron() 
    { 
     var scheduler = new TestScheduler(); 
     var cron = "* * * * *"; 
     var values = new List<int>(); 
     var disp = ObservableCron.Cron(cron, scheduler).Subscribe(values.Add); 
     scheduler.AdvanceBy(TimeSpan.TicksPerMinute - 1); 
     scheduler.AdvanceBy(1); 
     scheduler.AdvanceBy(1); 
     Assert.IsTrue(values.Count> 0); 
    } 
} 
+0

他们道理给我。这真的是你的问题吗? – Enigmativity 2014-10-28 00:36:51

+0

已更新。我遇到了测试该功能的问题。一旦'IScheduler'介入,我似乎错过了一些东西。 – 2014-10-28 00:53:08

回答

1

它看起来像的问题相结合。首先,我使用的Observable.Generate过载需要参数Func<int,DateTimeOffset>来确定下次触发。根据调度程序的本地时间,我传入了一个新的DateTimeOffset,而不是Utc,这导致新的'DateTimeOffset'发生偏移。有关说明,请参见this question。正确的函数如下所示:

public static IObservable<int> Cron(string cron, IScheduler scheduler) 
{ 
    var schedule = CrontabSchedule.Parse(cron); 
    return Observable.Generate(0, d => true, d => d + 1, d => d, 
     d => new DateTimeOffset(schedule.GetNextOccurrence(scheduler.Now.UtcDateTime)), scheduler); 
} 

至于测试进行,我想出了一个演示的意图更好一点:

[TestMethod] 
public void TestCronInterval() 
{ 
    var scheduler = new TestScheduler(); 
    var end = scheduler.Now.UtcDateTime.AddMinutes(10); 
    const string cron = "*/5 * * * *"; 
    var i = 0; 
    var seconds = 0; 
    var sub = ObservableCron.Cron(cron, scheduler).Subscribe(x => i++); 
    while (i < 2) 
    { 
     seconds++; 
     scheduler.AdvanceBy(TimeSpan.TicksPerSecond); 
    } 
    Assert.IsTrue(seconds == 600); 
    Assert.AreEqual(end, scheduler.Now.UtcDateTime); 
    sub.Dispose(); 
} 
0

首先,scheduler.Now.DateTime是不会给你在你的单元测试真正次用TestScheduler。它将根据一些预定义的开始时间为您提供虚拟时间。您可能应该使用AdvanceTo来初始化时钟,使其与您的crontab测试数据相对应。

对于此示例测试,这可能不是你的问题。您的问题很可能是您正在按照Tick的粒度编写测试。这很少奏效。因为对于TestScheduler,如果计划的操作发生在Tick t上,该计划执行“立即”执行的另一个操作,那么下一个操作将不会实际执行,直到Tick t+1。如果该行动安排了另一个行动,那么它将不会执行,直到打勾t+2等等。所以,除非您完全明白Generate的工作安排,否则您希望避免编写剔号测试。

请尝试按照您测试的代码支持的粒度进行测试。在这种情况下,我认为这只是几分钟...所以写下你的测试前进59秒,看看什么都没有发生,然后再提前2秒,看看你是否得到了你的预期。或者,更好的是,使用TestScheduler.CreateObserver方法,只是一次提前

var scheduler = new TestScheduler(); 

// set the virtual clock to something that corresponds with my test crontab data 
scheduler.AdvanceTo(someDateTimeOffset); 

// now your tests... 
var cron = "* * * * *"; 
var observer = scheduler.CreateObserver(); 

var disp = ObservableCron.Cron(cron, scheduler).Subscribe(observer); 

// advance the clock by 61 seconds 
scheduler.AdvanceBy(TimeSpan.FromSeconds(61).Ticks); 

// check the contents of the observer 
Assert.IsTrue(observer.Messages.Count > 0);