2017-04-12 4 views
0

Ich habe einen Code auf Git Hub, der hier gefunden werden kann: https://github.com/Shaunus87/SyncTest, die meinen Prototyp-Code und meine Einheit Tests dafür enthält.Unit Test Synchroncode hat eine Race Condition?

ich meine Synchroncode im Wesentlichen erklären, ein Ereignis, Haken, eine Methode aufrufen, die schließlich das Ereignis nennen, und behaupten, ob das Ereignis aufgerufen wurde oder nicht:

 bool called = false; 

     var testBinsToVend = GetRoboBins(); 
     var vendHelper = new VendingHelper(null, testBinsToVend, VendType.Issue); 

     vendHelper.Complete += delegate() { 
      called = true; 
     }; 

     vendHelper.DoVending(); 

     Assert.IsTrue(called); 

der gesamte Code synchron ist (wie Soweit ich weiß), aber wenn ich den Test ausführen, schlägt es fehl, wenn ich durch es debuggen, es passiert ...

Ich habe ein paar Dinge ausprobiert und es scheint entweder a) mein Code ist heimlich async und Ich habe eine Race-Bedingung oder b) wenn ich den Code ausführe, beschließt er, die Hälfte der Events nicht auszuführen?

Was zur Hölle?

Edit: ich auch versucht habe, wie unten ein manuelles Reset-Ereignis Einstellung:

 bool called = false; 
     var done = new ManualResetEvent(false); 

     var testBinsToVend = GetRoboBins(); 
     var vendHelper = new VendingHelper(null, testBinsToVend, VendType.Issue); 

     vendHelper.Complete += delegate() { 
      called = true; 
      done.Set(); 
     }; 

     vendHelper.DoVending(); 

     done.WaitOne(); 
     Assert.IsTrue(called); 
     //was complete called? 
     Assert.AreEqual(true, vendHelper.Bins.All(x => x.State != VendState.Pending)); 

aber da es eine Zeile der Ausführung, wenn done.WaitOne(); getroffen wird der Test nie die Assert.IsTrue(called); Linie erreicht.

+2

Ich fand Hinweise auf 'System.Threading.Timer' drin, Sie ** sicher ** Ihr Code ist synchron? Von wo wird dieses Ereignis ausgelöst? Inside [Timer_Tick] (https://github.com/Shaunus87/SyncTest/blob/master/eVendVendingMachines/BaseVendingMachine.cs#L182)? Setzen Sie einen Haltepunkt auf diese Zeile "called = true;" und überprüfen Sie den Aufruf-Stack. Dadurch erhalten Sie wahrscheinlich alles, was Sie beim Debuggen wissen müssen. –

Antwort

2

Es gibt ein Problem in der Business-Logik:

private CommCommand GetLastCommand(List<CommCommand> cmds, DateTime since) { 
return cmds.Where(x => x.DateTime > since) 
      .OrderByDescending(x => x.DateTime) 
      .FirstOrDefault(); 
} 

DateTime.Now hat nur eine Auflösung von etwa 20 ms oder so voreingestellt. Was bedeutet, dass Ihre Nachricht empfangen wird, lange bevor DateTime > since wahr wäre. Wenn Sie durch den Code gehen, wird das Timing angepasst - das Send passiert viel länger nach dem ursprünglichen Empfang.

Sie können sich nicht auf DateTime.Now für die Nachrichtenreihenfolge verlassen. Es hat einfach nicht genug Genauigkeit. Wenn Sie wirklich glauben, dass Sie sich auf die Reihenfolge von Senden und Empfangen für die Bestellung verlassen können (das heißt, die Maschine antwortet niemals, bevor sie dazu aufgefordert wird), ersetzen Sie sie stattdessen durch einen einfachen Zähler.

+0

Ja !! Ich wollte gerade fragen, wie du denkst, ich sollte es bestellen, dann sah ich, wie deine Bearbeitung auftaucht. Vielen Dank, ich hatte keine Ahnung, dass DateTime für solche Sachen nicht genau war. Wie hast du eigentlich die DateTimes entdeckt? War das Problem? (Ich habe meinen Kopf eine Wand hochgeklopft, weil ich denke, dass es eine Wettlaufbedingung ist.) – Smithy

+1

@Smithy Nun, zuerst habe ich einen Breakpoint in den Complete-Handler gelegt - also würde ich jetzt wirklich nicht aufgerufen. Dann stellte ich sicher, dass der Test nicht endete, bevor er eine Chance hatte zu laufen, nur weil es wirklich ein asynchrones Problem gab. Dann überprüfte ich, wo der Handler * angeblich * angerufen werden sollte, und verfolgte, welche von denen, von denen Sie erwarteten, dass sie angehoben wurden. Dann habe ich die Vorbedingungen überprüft und immer dann ausgedruckt, wenn eine Nachricht gesendet wurde ('Debug.WriteLine' ist nützlich). Schließlich habe ich einen Breakpoint in die WorkOnResponse für FET gesetzt (der letzte Befehl, der richtig funktioniert hat), und sah, dass 'lastRecCmd' null ist ... – Luaan

+1

@Smithy ... während' receivedCommands' nicht leer waren und tatsächlich die erwartete letzte enthielt Antwort. Das machte deutlich, dass "GetLastCommand" die Schuld trägt - und als ich den 'DateTime'-Vergleich sah, brauchte ich nicht weiter zu graben.Debugging ist ein bisschen eine Kunst: D – Luaan