2013-06-13 9 views
5

Sto provando a scrivere test unitari su un'implementazione personalizzata SynchronizationContext.Come posso verificare che le implementazioni siano sincrone o asincrone laddove richiesto?

Le due operazioni importanti questa classe sono Send e Post, dove Send invoca un delegato sincrono e Post richiama un delegato in modo asincrono.

Vorrei scrivere test di unità per verificare questo comportamento, che i delegati siano stati eseguiti in modo sincrono o asincrono. Non voglio che i test facciano affidamento sui ritardi per i casi di successo, perché prolunga artificialmente i test in esecuzione (ma è ragionevole che i guasti causino un ritardo) durante l'esecuzione.

Inizialmente ho pensato di utilizzare le attività per segnalare l'esecuzione di un delegato:

var tcs = new TaskCompletionSource<object>(); 

var context = new CustomSynchronizationContext(); 

context.Send((state) => tcs.SetResult(null), null); 

// Task should already be completed! 
Assert.IsTrue(this.tcs.Task.IsCompleted); 

Tuttavia, questo non garantisce il delegato è stato non eseguito in modo asincrono molto rapidamente prima che il test runner potrebbe continuare.

Come posso organizzare un test di tutto il contesto per garantire che Send blocchi per il completamento del delegato e Post non lo fa, ma che i delegati sono sia invocato?

+1

+1 per la domanda e il nome "Tragedian" una persona che risolvere i problemi ... :) –

+0

Isn è possibile fare un postback di 'Thread.ManagedThreadId' su cui il delegato è stato eseguito? Se era uguale al thread principale, veniva eseguito in modo sincrono, altrimenti asincrono. – Davio

+0

Il contesto di sincronizzazione non eseguirà alcun delegato sul thread chiamante, quindi temo di no. I thread sono solo un dettaglio di implementazione. –

risposta

-1

Incorporando la mia idea:

var mainThreadId = Thread.ManagedThreadId; 
var sendThreadId; 
context.Send((state) => sendThreadId = Thread.ManagedThreadId); 
Assert.AreEqual(mainThreadId, sendThreadId); 

Non so se questo funziona davvero, si controlla.

+0

Il contesto di sincronizzazione non eseguirà alcun delegato sul thread chiamante, quindi temo di no. I thread sono solo un dettaglio di implementazione nella programmazione asincrona. –

+0

Sì, "sincrono" e "asincrono" non sono uguali a "sullo stesso thread" e "su un altro thread". Il metodo sincrono potrebbe eseguire il delegato su un altro thread e attendere in modo sincrono che finisca. – svick

3

Credo che è possibile ottenere questo utilizzando un paio di ManualResetEvents. Usando il codice qui sotto, il rallentamento si verifica solo se i test falliscono (i numeri sono piuttosto alti e potrebbero essere ridotti in modo sicuro). L'idea qui è che affermiamo l'ordine in cui devono accadere le cose che possono accadere solo se blocchiamo o non blocchiamo.

Per la prova sincrono:

var incall = new ManualResetEvent(false); 
var unblock = new ManualResetEvent(false); 
var context = new CustomSynchronizationContext(); 
var t = Task.Run(() => context.Send(state => 
{ 
    incall.Set(); 
    unblock.WaitOne(5000); 
}, null)); 
Assert.IsTrue(incall.WaitOne(1000)); 
Assert.IsFalse(t.Wait(10)); 
unblock.Set(); 
Assert.IsTrue(t.Wait(1000)); 

per il test asincrono:

var incall = new ManualResetEvent(false); 
var unblock = new ManualResetEvent(false); 
var context = new CustomSynchronizationContext(); 
var t = Task.Run(() =>context.Post(state => 
{ 
    incall.Set(); 
    unblock.WaitOne(5000); 
}, null)); 
Assert.IsTrue(incall.WaitOne(1000)); 
Assert.IsTrue(t.Wait(1000)); //This will timeout if unblock is blocking completion of the task 
unblock.Set(); 
+0

Ma un metodo asincrona poteva superare il tuo test sincrono, se il tempismo era giusto. – svick

+0

ti riferisci al fatto che lo sblocco può scadere prima e l'attività viene completata prima dell'ultimo controllo o qualche altro problema? – jageall

+0

No, durante il tuo secondo 'Assert()', il metodo asincrono potrebbe aver già avviato l'esecuzione (quindi 'incall.WaitOne()' completa immediatamente) ma anche non sono stati ancora restituiti, quindi 'Status' sarà ancora' Running'. – svick

Problemi correlati