2014-10-27 69 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);