From fb73c4305a58fcef81fa8a5406e97b1a61a3fe0b Mon Sep 17 00:00:00 2001 From: jam40jeff Date: Thu, 7 Sep 2017 03:27:00 -0400 Subject: [PATCH] Fix C# SwitchS and Split to not create node cycles. --- c#/src/Sodium/Sodium.Tests/CellTests.cs | 82 ++++++++++++++++---- c#/src/Sodium/Sodium.Tests/StreamTests.cs | 42 ++++++++++ c#/src/Sodium/Sodium/CellExtensionMethods.cs | 2 +- c#/src/Sodium/Sodium/Operational.cs | 2 +- 4 files changed, 112 insertions(+), 16 deletions(-) diff --git a/c#/src/Sodium/Sodium.Tests/CellTests.cs b/c#/src/Sodium/Sodium.Tests/CellTests.cs index 5b0e8f74..6c273fe1 100644 --- a/c#/src/Sodium/Sodium.Tests/CellTests.cs +++ b/c#/src/Sodium/Sodium.Tests/CellTests.cs @@ -301,26 +301,40 @@ public void TestDiscreteCellLoopThrowsException() } [Test] - public void TestStreamLoop() + public void TestDiscreteCellLoopSwitchS() { - StreamSink streamSink = new StreamSink(); - Stream s = Transaction.Run(() => + StreamSink addStreamSink = new StreamSink(); + DiscreteCell> cell = Transaction.Run(() => { - StreamLoop sl = new StreamLoop(); - DiscreteCell c = sl.Map(v => v + 2).Hold(0); - Stream s2 = streamSink.Snapshot(c, (x, y) => x + y); - sl.Loop(s2); - return s2; + DiscreteCellLoop> cellLoop = new DiscreteCellLoop>(); + DiscreteCell> cellLocal = + cellLoop.Map(oo => oo.Select(o => o.RemoveStream.MapTo(new[] { o })).Merge((x, y) => x.Concat(y).ToArray())).SwitchS().Map, IReadOnlyList>>(o => c => c.Except(o).ToArray()) + .Merge(addStreamSink.Map, IReadOnlyList>>(o => c => c.Concat(new[] { o }).ToArray()), (f, g) => c => g(f(c))) + .Snapshot(cellLoop, (f, c) => f(c)) + .Hold(new TestObject[0]); + cellLoop.Loop(cellLocal); + return cellLocal; }); List @out = new List(); - IListener l = s.Listen(@out.Add); - streamSink.Send(3); - streamSink.Send(4); - streamSink.Send(7); - streamSink.Send(8); + IListener l = cell.Listen(c => @out.Add(c.Count)); + TestObject t1 = new TestObject(1, 1); + addStreamSink.Send(t1); + TestObject t2 = new TestObject(2, 2); + addStreamSink.Send(t2); + TestObject t3 = new TestObject(3, 3); + addStreamSink.Send(t3); + t2.Remove(); + TestObject t4 = new TestObject(4, 4); + Transaction.RunVoid(() => + { + addStreamSink.Send(t4); + t3.Remove(); + }); + TestObject t5 = new TestObject(5, 5); + addStreamSink.Send(t5); l.Unlisten(); - CollectionAssert.AreEqual(new[] { 3, 9, 18, 28 }, @out); + CollectionAssert.AreEqual(new[] { 0, 1, 2, 3, 2, 2, 3 }, @out); } [Test] @@ -1138,6 +1152,46 @@ public void SwitchSCatchFirst() CollectionAssert.AreEqual(new[] { 2, 13, 14, 5 }, output); } + [Test] + public void SwitchSCatchFirstBefore() + { + List output = new List(); + + ValueTuple, StreamSink, StreamSink, CellSink>, IListener> t = Transaction.Run(() => + { + StreamSink c1 = Stream.CreateSink(); + StreamSink c2 = Stream.CreateSink(); + CellSink> s = Cell.CreateSink(c1.AsStream()); + + c1.Send(2); + c2.Send(12); + s.Send(c2); + + Stream c = s.SwitchS(); + + IListener l = c.Listen(output.Add); + + return ValueTuple.Create(c, c1, c2, s, l); + }); + + t.Item2.Send(3); + t.Item3.Send(13); + + Transaction.RunVoid(() => + { + t.Item2.Send(4); + t.Item3.Send(14); + t.Item4.Send(t.Item2); + }); + + t.Item2.Send(5); + t.Item3.Send(15); + + t.Item5.Unlisten(); + + CollectionAssert.AreEqual(new[] { 2, 13, 14, 5 }, output); + } + [Test] public void SwitchEarlySCatchFirst() { diff --git a/c#/src/Sodium/Sodium.Tests/StreamTests.cs b/c#/src/Sodium/Sodium.Tests/StreamTests.cs index 9bf8ccf3..0f8e3f38 100644 --- a/c#/src/Sodium/Sodium.Tests/StreamTests.cs +++ b/c#/src/Sodium/Sodium.Tests/StreamTests.cs @@ -778,5 +778,47 @@ public async Task TestListenAsync() l2.Unlisten(); resultsAndCalled.Item3.Unlisten(); } + + [Test] + public void TestStreamLoop() + { + StreamSink streamSink = new StreamSink(); + Stream s = Transaction.Run(() => + { + StreamLoop sl = new StreamLoop(); + DiscreteCell c = sl.Map(v => v + 2).Hold(0); + Stream s2 = streamSink.Snapshot(c, (x, y) => x + y); + sl.Loop(s2); + return s2; + }); + List @out = new List(); + IListener l = s.Listen(@out.Add); + streamSink.Send(3); + streamSink.Send(4); + streamSink.Send(7); + streamSink.Send(8); + l.Unlisten(); + + CollectionAssert.AreEqual(new[] { 3, 9, 18, 28 }, @out); + } + + [Test] + public void TestStreamLoopDefer() + { + StreamSink streamSink = new StreamSink(); + Stream stream = Transaction.Run(() => + { + StreamLoop streamLoop = new StreamLoop(); + Stream streamLocal = Operational.Defer(streamSink.OrElse(streamLoop).Filter(v => v < 5).Map(v => v + 1)); + streamLoop.Loop(streamLocal); + return streamLocal; + }); + List @out = new List(); + IListener l = stream.Listen(@out.Add); + streamSink.Send(2); + l.Unlisten(); + + CollectionAssert.AreEqual(new[] { 3, 4, 5 }, @out); + } } } \ No newline at end of file diff --git a/c#/src/Sodium/Sodium/CellExtensionMethods.cs b/c#/src/Sodium/Sodium/CellExtensionMethods.cs index 57e37d28..3766b308 100644 --- a/c#/src/Sodium/Sodium/CellExtensionMethods.cs +++ b/c#/src/Sodium/Sodium/CellExtensionMethods.cs @@ -67,7 +67,7 @@ public static Stream SwitchS(this Cell> csa) }); }; trans1.Prioritized(new Node(), trans2 => hInitial(trans2, csa.SampleNoTransaction())); - IListener l1 = csa.Updates(trans1).Listen(@out.Node, trans1, h, false); + IListener l1 = csa.Updates(trans1).Listen(new Node(), trans1, h, false); return @out.UnsafeAttachListener(l1).UnsafeAttachListener(currentListener); }, false); } diff --git a/c#/src/Sodium/Sodium/Operational.cs b/c#/src/Sodium/Sodium/Operational.cs index a1700c6f..a3592111 100644 --- a/c#/src/Sodium/Sodium/Operational.cs +++ b/c#/src/Sodium/Sodium/Operational.cs @@ -64,7 +64,7 @@ public static Stream Defer(Stream s) public static Stream Split(Stream s) where TCollection : IEnumerable { Stream @out = new Stream(s.KeepListenersAlive); - IListener l1 = s.Listen(@out.Node, (trans, aa) => + IListener l1 = s.Listen(new Node(), (trans, aa) => { int childIx = 0; foreach (T a in aa)