private static List<Verification> SetupVerifications(Setup setup, EventStream stream, Mono.CSharp.Evaluator evaluator, string tab) { var verifications = new List<Verification>(); // First set the verifications. foreach (var verifyList in setup.Verify) { var verifyBuilder = new StringBuilder(@" var func = new Func<IEventStream, IObservable<Unit>>(stream => "); var filters = new List<string>(); for (int i = 0; i < verifyList.Then.Count; i++) { var var = ((char)(97 + i)).ToString(); // 97 == 'a' var verify = verifyList.Then[i]; var topicType = setup.Topics.Find(verify.Topic); Assert.True(topicType != TopicType.Unknown, "Undeclared topic '" + verify.Topic + "'"); var codeType = Brain.TopicCodeTypes.Find(topicType); Assert.False((topicType == TopicType.Void && verify.Value != null), string.Format("Cannot specify a value to compare for void topic '{0}' in verification '{1}'", verify.Topic, verify)); verifyBuilder.Append(tab).AppendLine("from {var} in stream.Commands<{type}>(\"{topic}\", \"{devices}\")".FormatWith( new { var = var, type = codeType, topic = verify.Topic, devices = verify.Devices })); // For void topics there's no need to filter out payload values, // just getting a message with the given topic satisfies the query. if (topicType != TopicType.Void) { // In this case we need to take into account the value // received. filters.Add("{var} == {value}".FormatWith( new { var = var, value = FormatValue(topicType, verify.Value) })); } } if (filters.Count > 0) { verifyBuilder.Append(tab).Append("where "); if (verifyList.Negate) { verifyBuilder .Append("!(") .Append(string.Join(" && ", filters)) .AppendLine(")"); } else { verifyBuilder.AppendLine(string.Join(" && ", filters)); } } verifyBuilder .Append(tab).AppendLine("select Unit.Default") .AppendLine(");") .AppendLine() .Append("func;"); Tracer.Get<BrainExercise>().Verbose("Buit verification query: " + verifyBuilder.ToString()); var func = (Func<IEventStream, IObservable<Unit>>)evaluator.Evaluate(verifyBuilder.ToString()); var query = func(stream); var verification = new Verification { Expression = (verifyList.Negate ? "!" : "") + string.Join(" and ", verifyList.Then.Select(t => t.ToString())), Succeeded = verifyList.Negate, }; if (verifyList.Negate) query.Subscribe(_ => verification.Succeeded = false); else query.Subscribe(_ => verification.Succeeded = true); verifications.Add(verification); } return verifications; }
private void Run(Setup setup) { var stream = new EventStream(); stream.Of<IEventPattern<IDevice, IImpulse>>().Subscribe(x => Tracer.Get<BrainExercise>().Info("Impulse from {0}: {1}", x.Sender.Id, x.EventArgs)); stream.Of<ICommand<float>>().Subscribe(x => Tracer.Get<BrainExercise>().Info("Command: {0}", x)); stream.Of<ICommand<bool>>().Subscribe(x => Tracer.Get<BrainExercise>().Info("Command: {0}", x)); stream.Of<ICommand<string>>().Subscribe(x => Tracer.Get<BrainExercise>().Info("Command: {0}", x)); stream.Of<ICommand<Unit>>().Subscribe(x => Tracer.Get<BrainExercise>().Info("Command: {0}", x)); var devices = Mock.Of<IDeviceRegistry>(); var topics = setup.Topics; var state = new SystemState(); // Hook up event stream consumers that perform orthogonal operations. new ClockImpulses(Sensorium.Clock.Default).Connect(stream); new CommandToBytes().Connect(stream); new SensedToImpulse(Sensorium.Clock.Default, topics).Connect(stream); new SetSystemState(state).Connect(stream); var brain = new Brain(stream, devices, topics, state, Sensorium.Clock.Default); var evaluator = CodeEvaluator.Create(); var anyDeviceType = Guid.NewGuid().ToString(); var anyDevice = Mock.Of<IDevice>(d => d.Id == Guid.NewGuid().ToString() && d.Type == anyDeviceType); // Arrange behaviors foreach (var when in setup.Statements) { brain.Behave(when); } var tab = " "; // Arrange verifications var verifications = SetupVerifications(setup, stream, evaluator, tab); // Act by issuing messages. SendGivenMessages(setup, stream, anyDeviceType, anyDevice); // Assert verifications succeeded. verifications.ForEach(v => Assert.True(v.Succeeded, "Verification failed for: " + v.Expression)); }
private static void SendGivenMessages(Setup setup, EventStream stream, string anyDeviceType, IDevice anyDevice) { foreach (var given in setup.Given) { var type = setup.Topics.Find(given.Topic); Assert.True(type != TopicType.Unknown, "Undefined given topic " + given.Topic); ISensed impulse = null; // Special [bytes] format if (type == TopicType.Void) impulse = new Sensed(given.Topic, new byte[0]); else if (given.Value.StartsWith("[") && given.Value.EndsWith("]")) impulse = new Sensed(given.Topic, ParseHex(given.Value.Substring(1, given.Value.Length - 1).Replace(" ", ""))); else { switch (type) { case TopicType.Boolean: impulse = new Sensed(given.Topic, Payload.ToBytes(bool.Parse(given.Value))); break; case TopicType.Number: impulse = new Sensed(given.Topic, Payload.ToBytes(float.Parse(given.Value))); break; case TopicType.String: impulse = new Sensed(given.Topic, Payload.ToBytes(given.Value)); break; } } if (string.IsNullOrEmpty(given.Devices)) { var usedGiven = setup.ParsedStatements.SelectMany(s => s.When .Where(w => w.Topic == given.Topic && !string.IsNullOrEmpty(w.Devices)) .Select(w => w)); Assert.False(usedGiven.Any(), string.Format("Invalid configuration. Cannot set given impulse {0} to be emitted with no specific Device Id because impulses of the same topic are expected to be issued with device ids {1}.", given.ToString(), string.Join(", ", usedGiven.Select(w => w.Devices.ToString())))); stream.Push(anyDevice, impulse); } else { // Must issue one impulse for each of the given originating devices. given.Devices .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(id => id.Trim()) .Where(id => !string.IsNullOrEmpty(id)) .ToList() .ForEach(id => stream.Push( Mock.Of<IDevice>(d => d.Id == id && d.Type == anyDeviceType), impulse)); } } }