public void TestDocumentChange()
        {
            var exp1 = new WaitAssert();
            var exp2 = new WaitAssert();

            Db.AddDocumentChangeListener("doc1", (sender, args) =>
            {
                WriteLine("Reached document changed callback");
                exp2.RunAssert(() =>
                {
                    WriteLine("Waiting for exp1 in document changed callback");
                    exp1.WaitForResult(TimeSpan.FromSeconds(20)); // Test deadlock
                });
            });

            WriteLine("Triggering async save");
            var ignore = exp1.RunAssertAsync(() =>
            {
                WriteLine("Running async save");
                Db.Save(new MutableDocument("doc1"));
                WriteLine("Async save completed");
            });

            WriteLine("Waiting for exp1 in test method");
            exp1.WaitForResult(TimeSpan.FromSeconds(10));
        }
        private void RunReplication(ReplicatorConfiguration config, int expectedErrCode, C4ErrorDomain expectedErrDomain)
        {
            Misc.SafeSwap(ref _repl, new Replicator(config));
            _waitAssert = new WaitAssert();
            var token = _repl.AddChangeListener((sender, args) =>
            {
                _waitAssert.RunConditionalAssert(() =>
                {
                    VerifyChange(args, expectedErrCode, expectedErrDomain);
                    if (config.Continuous && args.Status.Activity == ReplicatorActivityLevel.Idle &&
                        args.Status.Progress.Completed == args.Status.Progress.Total)
                    {
                        ((Replicator)sender).Stop();
                    }

                    return(args.Status.Activity == ReplicatorActivityLevel.Stopped);
                });
            });

            _repl.Start();
            try {
                _waitAssert.WaitForResult(TimeSpan.FromSeconds(10));
            } catch {
                _repl.Stop();
                throw;
            } finally {
                _repl.RemoveChangeListener(token);
            }
        }
        public void TestDocumentChange()
        {
            var doc1 = new MutableDocument("doc1");

            doc1.SetString("name", "Scott");
            doc1 = Db.Save(doc1).ToMutable();

            var doc2 = new MutableDocument("doc2");

            doc2.SetString("name", "Daniel");
            doc2 = Db.Save(doc2).ToMutable();

            Db.AddDocumentChangeListener("doc1", DocumentChanged);
            Db.AddDocumentChangeListener("doc2", DocumentChanged);
            Db.AddDocumentChangeListener("doc3", DocumentChanged);

            _expectedDocumentChanges = new HashSet <string> {
                "doc1",
                "doc2",
                "doc3"
            };
            _wa = new WaitAssert();

            doc1.SetString("name", "Scott Tiger");
            Db.Save(doc1);

            Db.Delete(doc2);

            var doc3 = new MutableDocument("doc3");

            doc3.SetString("name", "Jack");
            Db.Save(doc3);

            _wa.WaitForResult(TimeSpan.FromSeconds(5));
        }
        public void TestDatabaseChange()
        {
            var wa = new WaitAssert();

            Db.AddChangeListener(null, (sender, args) =>
            {
                var docIDs = args.DocumentIDs;
                wa.RunAssert(() =>
                {
                    args.Database.Should().Be(Db);
                    docIDs.Should().HaveCount(10, "because that is the number of expected rows");
                });
            });

            Db.InBatch(() =>
            {
                for (uint i = 0; i < 10; i++)
                {
                    var doc = new MutableDocument($"doc-{i}");
                    doc.SetString("type", "demo");
                    Db.Save(doc);
                }
            });

            wa.WaitForResult(TimeSpan.FromSeconds(5));
        }
        public async Task TestRemoveDocumentChangeListener()
        {
            var doc1 = new MutableDocument("doc1");

            doc1.SetString("name", "Scott");
            Db.Save(doc1);

            var token = Db.AddDocumentChangeListener("doc1", DocumentChanged);

            _wa = new WaitAssert();
            _expectedDocumentChanges = new HashSet <string> {
                "doc1"
            };

            doc1.SetString("name", "Scott Tiger");
            Db.Save(doc1);
            _wa.WaitForResult(TimeSpan.FromSeconds(5));

            Db.RemoveChangeListener(token);

            _wa = new WaitAssert();
            _docCallbackShouldThrow = true;
            doc1.SetString("name", "Scott Pilgrim");
            Db.Save(doc1);

            await Task.Delay(500);

            _wa.CaughtExceptions.Should().BeEmpty("because otherwise too many callbacks happened");

            // Remove again
            Db.RemoveChangeListener(token);
        }
        public void TestDatabaseChange()
        {
            var exp1 = new WaitAssert();
            var exp2 = new WaitAssert();

            Db.AddChangeListener(null, (sender, args) =>
            {
                exp2.RunAssert(() =>
                {
                    exp1.WaitForResult(TimeSpan.FromSeconds(20)); // Test deadlock
                });
            });

            var ignore = exp1.RunAssertAsync(() =>
            {
                Db.Save(new MutableDocument("doc1"));
            });

            exp1.WaitForResult(TimeSpan.FromSeconds(10));
        }
        private void WithActiveReplicatorAndURLEndpointListeners(bool isCloseNotDelete)
        {
            WaitAssert waitIdleAssert1    = new WaitAssert();
            WaitAssert waitStoppedAssert1 = new WaitAssert();

            _listener = CreateListener();
            var _listener2 = CreateNewListener();

            _listener.Config.Database.ActiveStoppables.Count.Should().Be(2);
            _listener2.Config.Database.ActiveStoppables.Count.Should().Be(2);

            using (var doc1 = new MutableDocument("doc1"))
                using (var doc2 = new MutableDocument("doc2")) {
                    doc1.SetString("name", "Sam");
                    Db.Save(doc1);
                    doc2.SetString("name", "Mary");
                    OtherDb.Save(doc2);
                }

            var target  = new DatabaseEndpoint(Db);
            var config1 = CreateConfig(target, ReplicatorType.PushAndPull, true, sourceDb: OtherDb);
            var repl1   = new Replicator(config1);

            repl1.AddChangeListener((sender, args) => {
                waitIdleAssert1.RunConditionalAssert(() => {
                    return(args.Status.Activity == ReplicatorActivityLevel.Idle);
                });

                waitStoppedAssert1.RunConditionalAssert(() => {
                    return(args.Status.Activity == ReplicatorActivityLevel.Stopped);
                });
            });

            repl1.Start();

            waitIdleAssert1.WaitForResult(TimeSpan.FromSeconds(10));
            OtherDb.ActiveStoppables.Count.Should().Be(3);

            if (isCloseNotDelete)
            {
                OtherDb.Close();
            }
            else
            {
                OtherDb.Delete();
            }

            OtherDb.ActiveStoppables.Count.Should().Be(0);
            OtherDb.IsClosedLocked.Should().Be(true);

            waitStoppedAssert1.WaitForResult(TimeSpan.FromSeconds(30));
        }
        public void TestConcurrentCreateNCreateIndexDB()
        {
            const int nDocs = 1000;

            var exp1   = new WaitAssert();
            var ignore = exp1.RunAssertAsync(() =>
            {
                CreateDocs(nDocs, "Create").ToList();
            });

            Db.CreateIndex("sentence", IndexBuilder.FullTextIndex(FullTextIndexItem.Property("sentence")));
            exp1.WaitForResult(TimeSpan.FromSeconds(60));
        }
        public void TestConcurrentCreateNCompactDB()
        {
            const int nDocs = 1000;

            var exp1   = new WaitAssert();
            var ignore = exp1.RunAssertAsync(() =>
            {
                CreateDocs(nDocs, "Create").ToList();
            });

            Db.Compact();
            exp1.WaitForResult(TimeSpan.FromSeconds(60));
        }
        public void TestStopContinuousReplicator()
        {
            var config = CreateConfig(true, false, true);

            using (var r = new Replicator(config)) {
                var stopWhen = new[]
                {
                    ReplicatorActivityLevel.Connecting, ReplicatorActivityLevel.Busy,
                    ReplicatorActivityLevel.Idle, ReplicatorActivityLevel.Idle
                };

                foreach (var when in stopWhen)
                {
                    var stopped    = 0;
                    var waitAssert = new WaitAssert();
                    var token      = r.AddChangeListener((sender, args) =>
                    {
                        waitAssert.RunConditionalAssert(() =>
                        {
                            VerifyChange(args, 0, 0);

                            // On Windows, at least, sometimes the connection is so fast that Connecting never gets called
                            if ((args.Status.Activity == when ||
                                 (when == ReplicatorActivityLevel.Connecting && args.Status.Activity > when)) &&
                                Interlocked.Exchange(ref stopped, 1) == 0)
                            {
                                WriteLine("***** Stop Replicator *****");
                                ((Replicator)sender).Stop();
                            }

                            if (args.Status.Activity == ReplicatorActivityLevel.Stopped)
                            {
                                WriteLine("Stopped!");
                            }

                            return(args.Status.Activity == ReplicatorActivityLevel.Stopped);
                        });
                    });

                    WriteLine("***** Start Replicator *****");
                    r.Start();
                    try {
                        waitAssert.WaitForResult(TimeSpan.FromSeconds(5));
                    } finally {
                        r.RemoveChangeListener(token);
                    }

                    Task.Delay(100).Wait();
                }
            }
        }
        public void TestConcurrentCreateAndDeleteDB()
        {
            const int nDocs = 1000;

            var exp1   = new WaitAssert();
            var ignore = exp1.RunAssertAsync(() =>
            {
                Action a = () => CreateDocs(nDocs, "Create").ToList();
                a.Should().Throw <InvalidOperationException>();
            });

            Db.Delete();
            exp1.WaitForResult(TimeSpan.FromSeconds(60));
        }
        public void TestStatus()
        {
            ulong maxConnectionCount = 0UL;
            ulong maxActiveCount     = 0UL;

            //init and start a listener
            _listener = CreateListener(false);

            //listener is started at this point
            _listener.Status.ConnectionCount.Should().Be(0, "Listener's connection count should be 0 because no client connection has been established.");
            _listener.Status.ActiveConnectionCount.Should().Be(0, "Listener's active connection count should be 0 because no client connection has been established.");

            using (var doc1 = new MutableDocument())
                using (var doc2 = new MutableDocument()) {
                    doc1.SetString("name", "Sam");
                    Db.Save(doc1);
                    doc2.SetString("name", "Mary");
                    OtherDb.Save(doc2);
                }

            var targetEndpoint = _listener.LocalEndpoint();
            var config         = new ReplicatorConfiguration(Db, targetEndpoint);

            using (var repl = new Replicator(config)) {
                var waitAssert = new WaitAssert();
                var token      = repl.AddChangeListener((sender, args) =>
                {
                    WriteLine($"Yeehaw {_listener.Status.ConnectionCount} / {_listener.Status.ActiveConnectionCount}");

                    maxConnectionCount = Math.Max(maxConnectionCount, _listener.Status.ConnectionCount);
                    maxActiveCount     = Math.Max(maxActiveCount, _listener.Status.ActiveConnectionCount);

                    waitAssert.RunConditionalAssert(() =>
                    {
                        return(args.Status.Activity == ReplicatorActivityLevel.Stopped);
                    });
                });

                repl.Start();
                while (repl.Status.Activity != ReplicatorActivityLevel.Busy)
                {
                    Thread.Sleep(100);
                }

                // For some reason running on mac throws off the timing enough so that the active connection count
                // of 1 is never seen.  So record the value right after it becomes busy.
                maxConnectionCount = Math.Max(maxConnectionCount, _listener.Status.ConnectionCount);
                maxActiveCount     = Math.Max(maxActiveCount, _listener.Status.ActiveConnectionCount);

                try {
                    waitAssert.WaitForResult(TimeSpan.FromSeconds(100));
                } finally {
                    repl.RemoveChangeListener(token);
                }
            }

            maxConnectionCount.Should().Be(1);
            maxActiveCount.Should().Be(1);

            //stop the listener
            _listener.Stop();
            _listener.Status.ConnectionCount.Should().Be(0, "Listener's connection count should be 0 because the connection is stopped.");
            _listener.Status.ActiveConnectionCount.Should().Be(0, "Listener's active connection count should be 0 because the connection is stopped.");
        }