public void CheckpointableStateManager_UnloadCanBeRetried()
        {
            var i    = 0;
            var fail = true;

            var e = new TestEngine(
                (_, ct, p) => Task.FromResult(true),
                (_, ct, p) => Task.FromResult(true),
                async p =>
            {
                if (Volatile.Read(ref fail))
                {
                    var j = Interlocked.Increment(ref i) - 1;
                    throw new Exception("Oops! " + j);
                }

                await Task.Yield();
            }
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            try
            {
                s.UnloadAsync().Wait();
                Assert.Fail();
            }
            catch (AggregateException ex)
            {
                ex.Handle(err => err.Message == "Oops! 0");
            }

            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.CheckpointAsync(new TestWriter())).Wait();
            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.RecoverAsync(new TestReader())).Wait();

            try
            {
                s.UnloadAsync().Wait();
                Assert.Fail();
            }
            catch (AggregateException ex)
            {
                ex.Handle(err => err.Message == "Oops! 1");
            }

            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.CheckpointAsync(new TestWriter())).Wait();
            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.RecoverAsync(new TestReader())).Wait();

            Volatile.Write(ref fail, false);

            s.UnloadAsync().Wait();
            Assert.AreEqual(2, Volatile.Read(ref i));

            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.CheckpointAsync(new TestWriter())).Wait();
            Assert.ThrowsExceptionAsync <InvalidOperationException>(() => s.RecoverAsync(new TestReader())).Wait();

            s.UnloadAsync().Wait();
            Assert.AreEqual(2, Volatile.Read(ref i));
        }
        public void CheckpointableStateManager_CheckpointCanBeCancelled()
        {
            var busy      = new TaskCompletionSource <bool>();
            var done      = new TaskCompletionSource <bool>();
            var cancelled = false;

            var e = new TestEngine(
                async(_, ct, p) => { ct.Register(() => cancelled = true); busy.SetResult(true); await done.Task; },
                (_, ct, p) => Task.FromResult(true),
                (p) => Task.FromResult(true)
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            var cts = new CancellationTokenSource();

            var t = s.CheckpointAsync(new TestWriter(), cts.Token);

            busy.Task.Wait();

            cts.Cancel();

            done.SetResult(true);
            t.Wait();

            Assert.IsTrue(cancelled);
        }
        public void CheckpointableStateManager_UnloadDuringCheckpointQueuesUp()
        {
            var checkpointDone = new TaskCompletionSource <bool>();

            var checkpointing = false;
            var unloading     = false;

            var e = new TestEngine(
                (_, ct, p) => { checkpointing = true; return(checkpointDone.Task); },
                (_, ct, p) => Task.FromResult(true),
                (p) => { unloading = true; return(Task.FromResult(true)); }
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            var c = s.CheckpointAsync(new TestWriter());

            Assert.IsTrue(checkpointing);

            var u1 = s.UnloadAsync();

            Assert.IsFalse(unloading);

            var u2 = s.UnloadAsync();

            Assert.IsFalse(unloading);

            checkpointDone.SetResult(true);
            c.Wait();

            Assert.IsTrue(unloading);
            u1.Wait();
            u2.Wait();
        }
        public void CheckpointableStateManager_ErrorDuringCheckpointKeepsEngineRunning()
        {
            var fail = true;

            var e = new TestEngine(
                async(_, ct, p) =>
            {
                if (Volatile.Read(ref fail))
                {
                    throw new ArithmeticException("Oops!");
                }

                await Task.Yield();
            },
                (_, ct, p) => Task.FromResult(true),
                (p) => Task.FromResult(true)
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            Assert.AreEqual(QueryEngineStatus.Running, s.Status);

            Assert.ThrowsExceptionAsync <ArithmeticException>(() => s.CheckpointAsync(new TestWriter())).Wait();

            Assert.AreEqual(QueryEngineStatus.Running, s.Status);

            Assert.ThrowsExceptionAsync <ArithmeticException>(() => s.CheckpointAsync(new TestWriter())).Wait();

            Assert.AreEqual(QueryEngineStatus.Running, s.Status);

            Volatile.Write(ref fail, false);

            s.CheckpointAsync(new TestWriter()).Wait();

            Assert.AreEqual(QueryEngineStatus.Running, s.Status);
        }
        public void CheckpointableStateManager_CheckpointCanBeCancelledImmediately()
        {
            var e = new TestEngine(
                (_, ct, p) => Task.FromResult(true),
                (_, ct, p) => Task.FromResult(true),
                (p) => Task.FromResult(true)
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            var cts = new CancellationTokenSource();

            cts.Cancel();

            Assert.ThrowsExceptionAsync <OperationCanceledException>(() => s.CheckpointAsync(new TestWriter(), cts.Token)).Wait();
        }
        public void CheckpointableStateManager_StateWriterWrapper()
        {
            var engine = new WriterTestEngine();
            var writer = new TestWriter();

            var s = new CheckpointableStateManager(engine, new Uri("qe:/test"), traceSource: null);

            s.CheckpointAsync(writer).Wait();

            Assert.IsTrue(writer.Log.SequenceEqual(new[]
            {
                "GetItemWriter(foo, bar)",
                "DeleteItem(qux, baz)",
                "Rollback()",
                "CommitAsync()",
                "Dispose()",
            }));
        }
        public void CheckpointableStateManager_UnloadDuringCheckpointCausesWriterShutdown()
        {
            var checkpointDone = new TaskCompletionSource <bool>();
            var unloadStarted  = new TaskCompletionSource <bool>();

            var checkpointing = false;
            var unloading     = false;

            var e = new TestEngine(
                async(w, ct, p) =>
            {
                checkpointing = true;
                await unloadStarted.Task;
                Assert.ThrowsException <EngineUnloadedException>(() => w.DeleteItem("foo", "bar"));
                Assert.ThrowsException <EngineUnloadedException>(() => w.GetItemWriter("foo", "bar"));
                Assert.ThrowsException <EngineUnloadedException>(() => w.Rollback());
                await Assert.ThrowsExceptionAsync <EngineUnloadedException>(() => w.CommitAsync());
                await checkpointDone.Task;
            },
                (_, ct, p) => Task.FromResult(true),
                (p) => { unloading = true; return(Task.FromResult(true)); }
                );

            var s = new CheckpointableStateManager(e, new Uri("qe:/test"), traceSource: null);

            var c = s.CheckpointAsync(new TestWriter());

            Assert.IsTrue(checkpointing);

            var u = s.UnloadAsync();

            Assert.IsFalse(unloading);
            unloadStarted.SetResult(true);

            checkpointDone.SetResult(true);
            c.Wait();

            Assert.IsTrue(unloading);
            u.Wait();
        }