public void WhenObservingImageEvents_ThenEventsStartStreamingInForImages()
        {
            var resetEvent = new ManualResetEventSlim();

            Task.Factory.StartNew(() =>
            {
                var imageEvents = new List <ImageEvent>();

                var testScheduler = new TestScheduler();
                var shellExecutor = new Mock <IShellExecutor>();

                var outputStream = new Subject <string>();

                shellExecutor
                .Setup(e => e.ObserveStandardOutput(It.IsAny <string>(), It.IsAny <string>()))
                .Returns(outputStream);

                testScheduler.Schedule(TimeSpan.FromTicks(1), () => outputStream.OnNext("{\"status\":\"tag\",\"id\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Type\":\"image\",\"Action\":\"untag\",\"Actor\":{\"ID\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Attributes\":{\"name\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\"}},\"scope\":\"local\",\"time\":1522790236,\"timeNano\":1522790236084372200}"));
                testScheduler.Schedule(TimeSpan.FromTicks(2), () => outputStream.OnNext("{\"status\":\"untag\",\"id\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Type\":\"image\",\"Action\":\"untag\",\"Actor\":{\"ID\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Attributes\":{\"name\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\"}},\"scope\":\"local\",\"time\":1522790236,\"timeNano\":1522790236084372200}"));
                testScheduler.Schedule(TimeSpan.FromTicks(3), () => outputStream.OnNext("{\"status\":\"delete\",\"id\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Type\":\"image\",\"Action\":\"delete\",\"Actor\":{\"ID\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\",\"Attributes\":{\"name\":\"sha256:ecea3d792cd143caccdf16935cfa1e4d0ec566a6d9e64eac25dfe9087c806702\"}},\"scope\":\"local\",\"time\":1522790236,\"timeNano\":1522790236135375000}"));
                testScheduler.Schedule(TimeSpan.FromTicks(4), () => outputStream.OnNext("{\"status\":\"pull\",\"id\":\"hello-world:latest\",\"Type\":\"image\",\"Action\":\"pull\",\"Actor\":{\"ID\":\"hello-world:latest\",\"Attributes\":{\"name\":\"hello-world\"}},\"scope\":\"local\",\"time\":1522790368,\"timeNano\":1522790368874018400}"));

                var subscription = new EventsStreamer(shellExecutor.Object).GetImageEventsObservable()
                                   .ObserveOn(Scheduler.Default)
                                   .Subscribe(x => { imageEvents.Add(x); });

                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                imageEvents.Count.Should().Be(1);
                imageEvents.ElementAt(0).ShortId.Should().Be("ecea3d792cd1");
                imageEvents.ElementAt(0).EventStatus.Should().Be(ImageEventStatus.Tag);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                imageEvents.Count.Should().Be(2);
                imageEvents.ElementAt(1).ShortId.Should().Be("ecea3d792cd1");
                imageEvents.ElementAt(1).EventStatus.Should().Be(ImageEventStatus.Untag);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                imageEvents.Count.Should().Be(3);
                imageEvents.ElementAt(2).ShortId.Should().Be("ecea3d792cd1");
                imageEvents.ElementAt(2).EventStatus.Should().Be(ImageEventStatus.Delete);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                imageEvents.Count.Should().Be(4);
                imageEvents.ElementAt(3).ShortId.Should().Be("ecea3d792cd1");
                imageEvents.ElementAt(3).EventStatus.Should().Be(ImageEventStatus.Pull);

                resetEvent.Set();
            });

            resetEvent.Wait();
            resetEvent.Dispose();
        }
        public void WhenObservingContainerEvents_ThenEventsStartStreamingInForContainers()
        {
            var resetEvent = new ManualResetEventSlim();

            Task.Factory.StartNew(() =>
            {
                var containerEvents = new List <ContainerEvent>();

                var testScheduler = new TestScheduler();
                var shellExecutor = new Mock <IShellExecutor>();

                var outputStream = new Subject <string>();

                shellExecutor
                .Setup(e => e.ObserveStandardOutput(It.IsAny <string>(), It.IsAny <string>()))
                .Returns(outputStream);

                // start
                testScheduler.Schedule(TimeSpan.FromTicks(1), () => outputStream.OnNext("{\"Type\":\"network\",\"Action\":\"connect\",\"Actor\":{ \"ID\":\"b11fbcc4b8d3510a3035522ddf9e2f8408a4dcfb72a3bf64a7145388fefc1019\",\"Attributes\":{ \"container\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"name\":\"nat\",\"type\":\"nat\"} },\"scope\":\"local\",\"time\":1522704104,\"timeNano\":1522704104089924100}"));
                testScheduler.Schedule(TimeSpan.FromTicks(2), () => outputStream.OnNext("{\"status\":\"start\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"start\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704105,\"timeNano\":1522704105577256600}"));
                // stop
                testScheduler.Schedule(TimeSpan.FromTicks(3), () => outputStream.OnNext("{\"status\":\"kill\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"kill\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\",\"signal\":\"15\"} },\"scope\":\"local\",\"time\":1522704122,\"timeNano\":1522704122497254900}"));
                testScheduler.Schedule(TimeSpan.FromTicks(4), () => outputStream.OnNext("{\"status\":\"die\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"die\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"exitCode\":\"3221225786\",\"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704122,\"timeNano\":1522704122810754200}"));
                testScheduler.Schedule(TimeSpan.FromTicks(5), () => outputStream.OnNext("{\"Type\":\"network\",\"Action\":\"disconnect\",\"Actor\":{ \"ID\":\"b11fbcc4b8d3510a3035522ddf9e2f8408a4dcfb72a3bf64a7145388fefc1019\",\"Attributes\":{ \"container\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"name\":\"nat\",\"type\":\"nat\"} },\"scope\":\"local\",\"time\":1522704122,\"timeNano\":1522704122852753400}"));
                testScheduler.Schedule(TimeSpan.FromTicks(6), () => outputStream.OnNext("{\"status\":\"stop\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"stop\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704122,\"timeNano\":1522704122862754200}"));
                // start
                testScheduler.Schedule(TimeSpan.FromTicks(7), () => outputStream.OnNext("{\"Type\":\"network\",\"Action\":\"connect\",\"Actor\":{ \"ID\":\"b11fbcc4b8d3510a3035522ddf9e2f8408a4dcfb72a3bf64a7145388fefc1019\",\"Attributes\":{ \"container\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"name\":\"nat\",\"type\":\"nat\"} },\"scope\":\"local\",\"time\":1522704147,\"timeNano\":1522704147964991000}"));
                testScheduler.Schedule(TimeSpan.FromTicks(8), () => outputStream.OnNext("{\"status\":\"start\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"start\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704149,\"timeNano\":1522704149430387400}"));
                // kill
                testScheduler.Schedule(TimeSpan.FromTicks(9), () => outputStream.OnNext("{\"status\":\"kill\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"kill\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\",\"signal\":\"9\"} },\"scope\":\"local\",\"time\":1522704166,\"timeNano\":1522704166982836400}"));
                testScheduler.Schedule(TimeSpan.FromTicks(10), () => outputStream.OnNext("{\"status\":\"die\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"die\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"exitCode\":\"4294967295\",\"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704166,\"timeNano\":1522704166983837400}"));
                testScheduler.Schedule(TimeSpan.FromTicks(11), () => outputStream.OnNext("{\"Type\":\"network\",\"Action\":\"disconnect\",\"Actor\":{ \"ID\":\"b11fbcc4b8d3510a3035522ddf9e2f8408a4dcfb72a3bf64a7145388fefc1019\",\"Attributes\":{ \"container\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"name\":\"nat\",\"type\":\"nat\"} },\"scope\":\"local\",\"time\":1522704167,\"timeNano\":1522704167017841200}"));
                // start
                testScheduler.Schedule(TimeSpan.FromTicks(12), () => outputStream.OnNext("{\"Type\":\"network\",\"Action\":\"connect\",\"Actor\":{ \"ID\":\"b11fbcc4b8d3510a3035522ddf9e2f8408a4dcfb72a3bf64a7145388fefc1019\",\"Attributes\":{ \"container\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"name\":\"nat\",\"type\":\"nat\"} },\"scope\":\"local\",\"time\":1522704186,\"timeNano\":1522704186955939900}"));
                testScheduler.Schedule(TimeSpan.FromTicks(13), () => outputStream.OnNext("{\"status\":\"start\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"start\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704188,\"timeNano\":1522704188388571200}"));
                //pause
                testScheduler.Schedule(TimeSpan.FromTicks(14), () => outputStream.OnNext("{\"status\":\"pause\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"pause\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704207,\"timeNano\":1522704207487173200}"));
                // unpause
                testScheduler.Schedule(TimeSpan.FromTicks(15), () => outputStream.OnNext("{\"status\":\"unpause\",\"id\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"from\":\"dc360deba4fc\",\"Type\":\"container\",\"Action\":\"unpause\",\"Actor\":{ \"ID\":\"c82386cd35b33d95559b6f4bb217da1190e2754f42459b4cc9440f0fb69f5a50\",\"Attributes\":{ \"image\":\"dc360deba4fc\",\"name\":\"nifty_liskov\"} },\"scope\":\"local\",\"time\":1522704224,\"timeNano\":1522704224102358100}"));
                //destroy
                testScheduler.Schedule(TimeSpan.FromTicks(16), () => outputStream.OnNext("{\"status\":\"destroy\",\"id\":\"07e3caf36749ae53668119ca34da1e6667e8dbefd8ab9b8b20f0b69f5fc6a22a\",\"from\":\"sampledockerapp\",\"Type\":\"container\",\"Action\":\"destroy\",\"Actor\":{ \"ID\":\"07e3caf36749ae53668119ca34da1e6667e8dbefd8ab9b8b20f0b69f5fc6a22a\",\"Attributes\":{ \"com.docker.compose.config-hash\":\"375101fe5fa9794eeae29416c129d8bb6b324c5bbc7afe0f358a908766cd866d\",\"com.docker.compose.container-number\":\"1\",\"com.docker.compose.oneoff\":\"False\",\"com.docker.compose.project\":\"repos\",\"com.docker.compose.service\":\"sampledockerapp\",\"com.docker.compose.version\":\"1.18.0\",\"image\":\"sampledockerapp\",\"name\":\"repos_sampledockerapp_1\"} },\"scope\":\"local\",\"time\":1523089719,\"timeNano\":1523089719273580200}"));
                //create
                testScheduler.Schedule(TimeSpan.FromTicks(17), () => outputStream.OnNext("{\"status\":\"create\",\"id\":\"44eb386b5e509977426c369f3c57b99712b14c3ba2eff9bf5206326b50d4733f\",\"from\":\"8999c0c122da\",\"Type\":\"container\",\"Action\":\"create\",\"Actor\":{ \"ID\":\"44eb386b5e509977426c369f3c57b99712b14c3ba2eff9bf5206326b50d4733f\",\"Attributes\":{ \"image\":\"8999c0c122da\",\"name\":\"hungry_vaughan\"} },\"scope\":\"local\",\"time\":1523089889,\"timeNano\":1523089889551458100}"));

                var subscription = new EventsStreamer(shellExecutor.Object).GetContainerEventsObservable()
                                   .ObserveOn(Scheduler.Default)
                                   .Subscribe(x => { containerEvents.Add(x); });

                testScheduler.AdvanceBy(1);
                containerEvents.Count.Should().Be(0);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(1);
                containerEvents.ElementAt(0).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(0).EventStatus.Should().Be(ContainerEventStatus.Start);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(2);
                containerEvents.ElementAt(1).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(1).EventStatus.Should().Be(ContainerEventStatus.Kill);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(3);
                containerEvents.ElementAt(2).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(2).EventStatus.Should().Be(ContainerEventStatus.Die);
                testScheduler.AdvanceBy(2);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(4);
                containerEvents.ElementAt(3).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(3).EventStatus.Should().Be(ContainerEventStatus.Stop);
                testScheduler.AdvanceBy(2);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(5);
                containerEvents.ElementAt(4).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(4).EventStatus.Should().Be(ContainerEventStatus.Start);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(6);
                containerEvents.ElementAt(5).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(5).EventStatus.Should().Be(ContainerEventStatus.Kill);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(7);
                containerEvents.ElementAt(6).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(6).EventStatus.Should().Be(ContainerEventStatus.Die);
                testScheduler.AdvanceBy(3);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(8);
                containerEvents.ElementAt(7).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(7).EventStatus.Should().Be(ContainerEventStatus.Start);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(9);
                containerEvents.ElementAt(8).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(8).EventStatus.Should().Be(ContainerEventStatus.Pause);
                testScheduler.AdvanceBy(1);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(10);
                containerEvents.ElementAt(9).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(9).EventStatus.Should().Be(ContainerEventStatus.Unpause);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(11);
                containerEvents.ElementAt(10).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(10).EventStatus.Should().Be(ContainerEventStatus.Destroy);
                Thread.Sleep(100);
                containerEvents.Count.Should().Be(12);
                containerEvents.ElementAt(11).ShortId.Should().Be("c82386cd35b3");
                containerEvents.ElementAt(11).EventStatus.Should().Be(ContainerEventStatus.Create);

                resetEvent.Set();
            });

            resetEvent.Wait();
            resetEvent.Dispose();
        }