2014-10-27 2 views
1

Я хотел бы создать наблюдаемую последовательность с использованием реактивных расширений (RX) и NCrontab. Последовательность будет отличаться от примерно Observable.Timer() тем, что период и время не установлены. После прочтения this article кажется, что Observable.Generate() - это путь. Я имею в виду два варианта: один, который проходит в пределах границ и один, который работает вечно. Имеют ли смысл эти реализации?Наблюдаемая последовательность Cron

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

+0

Обновлено. У меня проблема с тестированием функции. Кажется, я что-то упускаю, когда участвует «IScheduler». –

ответ

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, когда запланированное действие происходит по тику t, которое планирует другое действие для выполнения «немедленно», следующее действие фактически не будет выполняться до тех пор, пока не будет указан тикер 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); 
Смежные вопросы