public void Successfully_ChangeInvalidToValid_Stored()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("1d20");
            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            EatExceptions(DiceErrorCode.ParseError, () => post2.AddRoll("qwerty"));
            post2.AddRoll("1d20", Roll9Conf);
            post2.Validate();

            stream.Dispose();
            stream = new MemoryStream();
            post2.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post3 = RollPost.Deserialize(stream);

            post3.AddRoll("1d20+1");
            post3.AddRoll("1d20", Roll20Conf);

            Assert.IsFalse(post3.Validate());
            Assert.AreEqual(post3.Stored[1], post3.Current[1]);
            Assert.AreEqual(1, post3.Diverged);
        }
        public void Successfully_PostsDiverge_Stored()
        {
            var post1  = new RollPost();
            var post2  = new RollPost();
            var stream = new MemoryStream();

            post1.AddRoll("1d20", Roll9Conf);
            post1.AddRoll("1d20", Roll9Conf);
            post2.AddRoll("1d20", Roll9Conf);
            post2.AddRoll("1d20", Roll1Conf);
            post1.Validate();
            post2.Validate();

            post1.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post3 = RollPost.Deserialize(stream);

            stream.Dispose();

            stream = new MemoryStream();
            post2.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post4 = RollPost.Deserialize(stream);

            stream.Dispose();

            Assert.IsTrue(post3.HasDivergedFrom(post4));
        }
        public void ThrowInvalidMacro_WhenInvalidDice_RollMacro()
        {
            var post = new RollPost();

            post.AddRoll("1d20");
            EvaluatePost("[roll:1:Q]", DiceErrorCode.InvalidMacro, post);
        }
        public void Successfully_NotValidate_WhenKeepingStoredPost()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            post2.AddRoll("1d20+1");
            EatExceptions(() => post2.AddRoll("q"));
            post2.Validate();

            stream.Dispose();
            stream = new MemoryStream();
            post2.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post3 = RollPost.Deserialize(stream);

            post3.AddRoll("1d20+1");
            EatExceptions(() => post3.AddRoll("q"));

            Assert.IsFalse(post3.Validate());
            Assert.AreEqual(post3.Stored[0], post3.Current[0]);
        }
        public void Successfully_PostsDontDiverge_Empty()
        {
            var post1 = new RollPost();
            var post2 = new RollPost();

            Assert.IsFalse(post1.HasDivergedFrom(post2));
        }
        public void Successfully_Validate_WhenNewPost()
        {
            var post = new RollPost();

            post.AddRoll("1d20");

            Assert.IsTrue(post.Validate());
        }
        public void Successfully_RollMacro_XNegative()
        {
            var post = new RollPost();

            post.AddRoll("1d20", Roll9Conf);
            post.AddRoll("[roll:-1] + 1");

            Assert.AreEqual("[roll:-1] + 1 => 9 + 1 => 10", post.Current[1].ToString());
        }
        public void ThrowInvalidMacro_WhenInvalidTag_RollMacro()
        {
            var post = new RollPost();

            post.AddRoll("1d20");
            // technically the same as InvalidDice in terms of implementation
            // adding as separate test just in case that changes in the future
            EvaluatePost("[roll:1:creetical]", DiceErrorCode.InvalidMacro, post);
        }
        public void Successfully_AttackRollExpression()
        {
            var post = new RollPost();

            post.AddRoll("1d20", Roll9Conf);
            post.AddRoll("if([roll:-1:critical], =0, 2d10, 2{2d10})+3", Roll9Conf);

            Assert.AreEqual("if([roll:-1:critical], =0, 2d10, 2{2d10}) + 3 => (9 + 9) + 3 => 21", post.Current[1].ToString());
        }
        public void Successfully_RollMacro_XY_Critical()
        {
            var post = new RollPost();

            post.AddRoll("2d20", Roll20Conf);
            post.AddRoll("[roll:1:1:critical]");

            Assert.AreEqual("[roll:1:1:critical] => 1 => 1", post.Current[1].ToString());
        }
        public void Successfully_PostsDiverge_LargerSecond()
        {
            var post1 = new RollPost();
            var post2 = new RollPost();

            post1.AddRoll("1d20", Roll1Conf);
            post2.AddRoll("1d20", Roll9Conf);
            post2.AddRoll("1d20", Roll9Conf);

            Assert.IsTrue(post1.HasDivergedFrom(post2));
        }
        public void Successfully_PostsDontDiverge_LargerFirst()
        {
            var post1 = new RollPost();
            var post2 = new RollPost();

            post1.AddRoll("1d20", Roll9Conf);
            post1.AddRoll("1d20", Roll9Conf);
            post2.AddRoll("1d20", Roll9Conf);

            Assert.IsFalse(post1.HasDivergedFrom(post2));
        }
        public void Successfully_RollMacro_XY()
        {
            var post = new RollPost();

            post.AddRoll("2d20", new RollerConfig()
            {
                GetRandomBytes = GetRNG(8, 9)
            });
            post.AddRoll("[roll:1:1] + 1");

            Assert.AreEqual("[roll:1:1] + 1 => 9 + 1 => 10", post.Current[1].ToString());
        }
        public void Successfully_RollMacro_ReadFailureFlag()
        {
            var post = new RollPost();

            post.AddRoll("2d10>5f1", new RollerConfig()
            {
                GetRandomBytes = GetRNG(5, 0)
            });
            post.AddRoll("[roll:1:failure]");

            Assert.AreEqual(1, post.Current[1].Value);
        }
        public void Successfully_NotValidate_WhenRemovingRolls()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            Assert.IsFalse(post2.Validate());
        }
        public void Successfully_Validate_WhenSavedPostMatches()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            post2.AddRoll("1d20");

            Assert.IsTrue(post2.Validate());
        }
        private static void EvaluatePost(string roll, DiceErrorCode error, RollPost post = null)
        {
            post = post ?? new RollPost();

            try
            {
                post.AddRoll(roll);
                Assert.Fail("Expected DiceException with error code {0}, but did not receive an exception.", error.ToString());
            }
            catch (DiceException e)
            {
                if (e.ErrorCode != error)
                {
                    Assert.Fail("Expected DiceException with error code {0}, but got error code {1}.", error.ToString(), e.ErrorCode.ToString());
                }
            }
        }
        public void Successfully_NotValidate_WhenChangingRollToInvalid()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            EatExceptions(DiceErrorCode.ParseError, () => post2.AddRoll("qwerty"));

            Assert.IsFalse(post2.Validate());
            Assert.AreEqual(RollResult.InvalidRoll, post2.Current[0]);
        }
        public void Successfully_RoundtripRollPost_NoValidate()
        {
            var formatter = new BinaryFormatter();
            var stream    = new MemoryStream();
            var post      = new RollPost();

            post.AddRoll("1d20+4");
            post.AddRoll("2d6+3");

            formatter.Serialize(stream, post);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = (RollPost)formatter.Deserialize(stream);

            CollectionAssert.AreEqual(post.Pristine.ToList(), post2.Pristine.ToList());
            CollectionAssert.AreEqual(post.Stored.ToList(), post2.Stored.ToList());
            CollectionAssert.AreEqual(post.Current.ToList(), post2.Current.ToList());
            Assert.AreEqual(post.Diverged, post2.Diverged);
        }
        public void Successfully_Validate_WhenChangingInvalidRollToValid()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            EatExceptions(DiceErrorCode.ParseError, () => post.AddRoll("qwerty"));
            post.AddRoll("1d20");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            post2.AddRoll("1d20");
            post2.AddRoll("1d20");

            Assert.IsTrue(post2.Validate());
            Assert.AreEqual(0, post2.Diverged);
        }
        public void TestCompression()
        {
            var ustream = new MemoryStream();
            var cstream = new MemoryStream();
            var post    = new RollPost();

            post.AddRoll("20d20+100d6");
            post.AddRoll("(4d8)d6+3");
            post.AddRoll("[roll:2]-2");
            post.AddRoll("1000d10000");

            post.Serialize(ustream);

            using (var dstream = new DeflateStream(cstream, CompressionLevel.Optimal, true))
            {
                post.Serialize(dstream);
            }

            Assert.IsTrue(cstream.Length < ustream.Length * 0.5, "Compression isn't significantly smaller");
        }
        public void Successfully_PreserveMetadata()
        {
            var metadata = "foobar";
            var stream   = new MemoryStream();
            var post     = new RollPost();

            post.AddRoll("1d20", null, new RollData()
            {
                Metadata = metadata
            });
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            post2.AddRoll("1d20");
            post2.Validate();

            Assert.AreEqual(metadata, (string)post2.Current[0].Metadata);
        }
        public void Successfully_RoundtripRollPost_ForPersistence()
        {
            var stream = new MemoryStream();
            var post   = new RollPost();

            post.AddRoll("20d20+4");
            post.AddRoll("2d6+3");
            post.AddRoll("[roll:2]-2");
            post.Validate();

            post.Serialize(stream);
            stream.Seek(0, SeekOrigin.Begin);
            var post2 = RollPost.Deserialize(stream);

            CollectionAssert.AreEqual(post.Pristine.ToList(), post2.Pristine.ToList());
            CollectionAssert.AreEqual(post.Current.ToList(), post2.Stored.ToList());
            CollectionAssert.AreEqual(new List <RollResult>(), post2.Current.ToList());
            Assert.AreEqual(0, post2.Diverged);

            post2.AddRoll("10d20+4");
            Assert.AreEqual(2, post2.Diverged);
        }