public void FormatMessage_caches_reused_pattern()
        {
            var parserMock = new Mock<IPatternParser>();
            var realParser = new PatternParser(new LiteralParser());
            parserMock.Setup(x => x.Parse(It.IsAny<StringBuilder>()))
                      .Returns((StringBuilder sb) => realParser.Parse(sb));
            var library = new FormatterLibrary();

            var subject = new MessageFormatter(patternParser: parserMock.Object, library: library, useCache: true);

            var pattern = "Hi {gender, select, male {Sir} female {Ma'am}}!";
            var actual = subject.FormatMessage(pattern, new { gender = "male" });
            Assert.Equal("Hi Sir!", actual);

            // '2' because it did not format "Ma'am" yet.
            parserMock.Verify(x => x.Parse(It.IsAny<StringBuilder>()), Times.Exactly(2));

            actual = subject.FormatMessage(pattern, new { gender = "female" });
            Assert.Equal("Hi Ma'am!", actual);
            parserMock.Verify(x => x.Parse(It.IsAny<StringBuilder>()), Times.Exactly(3));

            // '3' because it has cached all options
            actual = subject.FormatMessage(pattern, new { gender = "female" });
            Assert.Equal("Hi Ma'am!", actual);
            parserMock.Verify(x => x.Parse(It.IsAny<StringBuilder>()), Times.Exactly(3));
        }
        private static string getMessage(int count, object[] inputs, MessageFormatter formatter)
        {
            var sb = new StringBuilder();

            sb.AppendFormat("Expected {0} messages, but the following {1} were generated:", count, inputs.Length);
            sb.AppendLine();

            foreach (var input in inputs)
            {
                sb.AppendLine(formatter.FormatMessage(input));
            }

            sb.AppendLine("--End of expected messages--");

            return sb.ToString();
        }
        public void FormatMessage()
        {
            var patternParserMock = new Mock<IPatternParser>();
            var libraryMock = new Mock<IFormatterLibrary>();
            var collectionMock = new Mock<IFormatterRequestCollection>();
            var formatterMock1 = new Mock<IFormatter>();
            var formatterMock2 = new Mock<IFormatter>();
            var subject = new MessageFormatter(patternParserMock.Object, libraryMock.Object, false, locale: "en");

            // I can use a bogus plural argument. Mocks <3
            var pattern = "{name} has {messages, plural, 123}.";
            var expected = "Jeff has 123 messages.";
            var args = new Dictionary<string, object> { { "name", "Jeff" }, { "messages", 1 } };
            var requests = new[]
                           {
                               new FormatterRequest(
                                   new Literal(0, 5, 1, 7, new StringBuilder("name")), 
                                   "name", 
                                   null, 
                                   null), 
                               new FormatterRequest(
                                   new Literal(11, 33, 1, 7, new StringBuilder("messages, plural, 123")), 
                                   "messages", 
                                   "plural", 
                                   " 123")
                           };
            formatterMock1.Setup(x => x.Format("en", requests[0], args, subject)).Returns("Jeff");
            formatterMock2.Setup(x => x.Format("en", requests[1], args, subject)).Returns("123 messages");

            collectionMock.Setup(x => x.GetEnumerator()).Returns(EnumeratorMock(requests));

            libraryMock.Setup(x => x.GetFormatter(requests[0])).Returns(formatterMock1.Object);

            libraryMock.Setup(x => x.GetFormatter(requests[1])).Returns(formatterMock2.Object);
            patternParserMock.Setup(x => x.Parse(It.IsAny<StringBuilder>())).Returns(collectionMock.Object);

            // First request, and "name" is 4 chars.
            collectionMock.Setup(x => x.ShiftIndices(0, 4)).Callback(
                (int index, int length) => {
                    // The '- 2' is also done in the used implementation.
                    requests[1].SourceLiteral.ShiftIndices(length - 2, requests[0].SourceLiteral);
                });
            var actual = subject.FormatMessage(pattern, args);
            collectionMock.Verify(x => x.ShiftIndices(0, 4), Times.Once);
            Assert.Equal(expected, actual);
        }
        public void FormatMessage_debug()
        {
            var source = @"{gender, select, 
                           male {He}
                           female {She}
                           other {They}
                      } said: You have {count, plural, 
                            zero {no notifications}
                            one {just one notification}
                            =42 {a universal amount of notifications}
                            other {# notifications}
                      }. Have a nice day!";
            var expected = "He said: You have 5 notifications. Have a nice day!";
            var args = new Dictionary<string, object> { { "gender", "male" }, { "count", 5 } };
            var subject = new MessageFormatter(false);

            string result = subject.FormatMessage(source, args);
            Assert.Equal(expected, result);
        }
 public void FormatMessage_nesting_with_brace_escaping()
 {
     var subject = new MessageFormatter(false);
     var pattern = @"{s1, select, 
                         1 {{s2, select, 
                            2 {\{}
                         }}
                     }";
     var actual = subject.FormatMessage(pattern, new { s1 = 1, s2 = 2 });
     this.outputHelper.WriteLine(actual);
     Assert.Equal("{", actual);
 }
        public void FormatMessage_with_reflection_overload()
        {
            var subject = new MessageFormatter(false);
            var pattern = "You have {UnreadCount, plural, " + "zero {no unread messages}"
                          + "one {just one unread message}" + "other {# unread messages}" + "} today.";
            var actual = subject.FormatMessage(pattern, new { UnreadCount = 0 });
            Assert.Equal("You have no unread messages today.", actual);

            // The absence of UnreadCount means it will be treated as "zero".
            actual = subject.FormatMessage(pattern, new { });
            Assert.Equal("You have no unread messages today.", actual);

            actual = subject.FormatMessage(pattern, new { UnreadCount = 1 });
            Assert.Equal("You have just one unread message today.", actual);
            actual = subject.FormatMessage(pattern, new { UnreadCount = 2 });
            Assert.Equal("You have 2 unread messages today.", actual);

            actual = subject.FormatMessage(pattern, new { UnreadCount = 3 });
            Assert.Equal("You have 3 unread messages today.", actual);
        }
        public void ReadMe_test_to_make_sure_I_dont_look_like_a_fool()
        {
            {
                var mf = new MessageFormatter(false);
                var str = @"You have {notifications, plural,
                              zero {no notifications}
                              one {one notification}
                              =42 {a universal amount of notifications}
                              other {# notifications}
                            }. Have a nice day, {name}!";
                var formatted = mf.FormatMessage(
                    str, 
                    new Dictionary<string, object> { { "notifications", 4 }, { "name", "Jeff" } });
                Assert.Equal("You have 4 notifications. Have a nice day, Jeff!", formatted);
            }
            {
                var mf = new MessageFormatter(false);
                var str = @"You {NUM_ADDS, plural, offset:1
                              =0{didnt add this to your profile} 
                              zero{added this to your profile}
                              one{and one other person added this to their profile}
                              other{and # others added this to their profiles}
                          }.";
                var formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM_ADDS", 0 } });
                Assert.Equal("You didnt add this to your profile.", formatted);

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM_ADDS", 1 } });
                Assert.Equal("You added this to your profile.", formatted);

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM_ADDS", 2 } });
                Assert.Equal("You and one other person added this to their profile.", formatted);

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM_ADDS", 3 } });
                Assert.Equal("You and 2 others added this to their profiles.", formatted);
            }
            {
                var mf = new MessageFormatter(false);
                var str = @"{GENDER, select,
                                male {He}
                              female {She}
                               other {They}
                            } found {NUM_RESULTS, plural,
                                        one {1 result}
                                      other {# results}
                                    } in {NUM_CATEGORIES, plural,
                                              one {1 category}
                                            other {# categories}
                                         }.";
                var formatted = mf.FormatMessage(
                    str, 
                    new Dictionary<string, object>
                    {
                        { "GENDER", "male" }, 
                        { "NUM_RESULTS", 1 }, 
                        { "NUM_CATEGORIES", 2 }
                    });
                Assert.Equal(formatted, "He found 1 result in 2 categories.");

                formatted = mf.FormatMessage(
                    str, 
                    new Dictionary<string, object>
                    {
                        { "GENDER", "male" }, 
                        { "NUM_RESULTS", 1 }, 
                        { "NUM_CATEGORIES", 1 }
                    });
                Assert.Equal(formatted, "He found 1 result in 1 category.");

                formatted = mf.FormatMessage(
                    str, 
                    new Dictionary<string, object>
                    {
                        { "GENDER", "female" }, 
                        { "NUM_RESULTS", 2 }, 
                        { "NUM_CATEGORIES", 1 }
                    });
                Assert.Equal(formatted, "She found 2 results in 1 category.");
            }
            {
                var mf = new MessageFormatter(false);
                var str = @"Your {NUM, plural, one{message} other{messages}} go here.";
                var formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM", 1 } });
                Assert.Equal(formatted, "Your message go here.");

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "NUM", 3 } });
                Assert.Equal(formatted, "Your messages go here.");
            }
            {
                var mf = new MessageFormatter(false);
                var str = @"His name is {LAST_NAME}... {FIRST_NAME} {LAST_NAME}";
                var formatted = mf.FormatMessage(
                    str, 
                    new Dictionary<string, object> { { "FIRST_NAME", "James" }, { "LAST_NAME", "Bond" } });
                Assert.Equal(formatted, "His name is Bond... James Bond");
            }
            {
                var mf = new MessageFormatter(false);
                var str = @"{GENDER, select, male{He} female{She} other{They}} liked this.";
                var formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "GENDER", "male" } });
                Assert.Equal(formatted, "He liked this.");

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "GENDER", "female" } });
                Assert.Equal(formatted, "She liked this.");

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { { "GENDER", "somethingelse" } });
                Assert.Equal(formatted, "They liked this.");

                formatted = mf.FormatMessage(str, new Dictionary<string, object> { });
                Assert.Equal(formatted, "They liked this.");
            }
            {
                var mf = new MessageFormatter(true, "en");
                mf.Pluralizers["en"] = n => {
                    // ´n´ is the number being pluralized.
                    if (n == 0)
                    {
                        return "zero";
                    }

                    if (n == 1)
                    {
                        return "one";
                    }

                    if (n > 1000)
                    {
                        return "thatsalot";
                    }

                    return "other";
                };

                var actual =
                    mf.FormatMessage(
                        "You have {number, plural, thatsalot {a shitload of notifications} other {# notifications}}", 
                        new Dictionary<string, object> { { "number", 1001 } });
                Assert.Equal("You have a shitload of notifications", actual);
            }
        }
        public void FormatMessage(string source, Dictionary<string, object> args, string expected)
        {
            var subject = new MessageFormatter(false);

            // Warmup
            subject.FormatMessage(source, args);
            Benchmark.Start("Formatting", this.outputHelper);
            string result = subject.FormatMessage(source, args);
            Benchmark.End(this.outputHelper);
            Assert.Equal(expected, result);
            this.outputHelper.WriteLine(result);
        }
 public void FormatMessage_lets_non_ascii_characters_right_through()
 {
     var input = "中test中国话不用彁字。";
     var subject = new MessageFormatter(false);
     var actual = subject.FormatMessage(input, new Dictionary<string, object>());
     Assert.Equal(input, actual);
 }
        public void FormatMessage_using_real_parser_and_library_mock(string source, string expected)
        {
            var mockLibary = new Mock<IFormatterLibrary>();
            var dummyFormatter = new Mock<IFormatter>();
            var subject = new MessageFormatter(
                new PatternParser(new LiteralParser()), 
                mockLibary.Object, 
                false);

            var args = new Dictionary<string, object>();
            args.Add("name", "Jeff");
            dummyFormatter.Setup(x => x.Format("en", It.IsAny<FormatterRequest>(), args, "Jeff", subject))
                          .Returns("Jeff");
            mockLibary.Setup(x => x.GetFormatter(It.IsAny<FormatterRequest>())).Returns(dummyFormatter.Object);

            // Warm up
            Benchmark.Start("Warm-up", this.outputHelper);
            subject.FormatMessage(source, args);
            Benchmark.End(this.outputHelper);

            Benchmark.Start("Aaaand a few after warm-up", this.outputHelper);
            for (int i = 0; i < 1000; i++)
            {
                subject.FormatMessage(source, args);
            }

            Benchmark.End(this.outputHelper);

            Assert.Equal(expected, subject.FormatMessage(source, args));
        }
 public void DescribeTo(SpecInfo spec, MessageFormatter formatter)
 {
     spec.ReportExpectation(formatter.FormatMessage(_expected));
 }
        /// <summary>
        /// The benchmark.
        /// </summary>
        /// <param name="subject">
        /// The subject.
        /// </param>
        private void Benchmark(MessageFormatter subject)
        {
            var pattern = "\r\n----\r\nOh {name}? And if we were " + "to surround {gender, select, " + "male {his} "
                          + "female {her}" + "} name with \\{ and \\}, it would look "
                          + "like \\{{name}\\}? Yeah, I know {gender, select, " + "male {him} " + "female {her}"
                          + "}. {gender, select, " + "male {He's}" + "female {She's}" + "} got {messageCount, plural, "
                          + "zero {no messages}" + "one {just one message}" + "=42 {a universal amount of messages}"
                          + "other {uuhm... let's see.. Oh yeah, # messages - and here's a pound: \\#}" + "}!";
            int iterations = 100000;
            var args = new Dictionary<string, object>[iterations];
            var rnd = new Random();
            for (int i = 0; i < iterations; i++)
            {
                var val = rnd.Next(50);
                args[i] =
                    new
                    {
                        gender = val % 2 == 0 ? "male" : "female", 
                        name = val % 2 == 0 ? "Jeff" : "Amanda", 
                        messageCount = val
                    }.ToDictionary();
            }

            TestHelpers.Benchmark.Start("Formatting message " + iterations + " times, no warm-up.", this.outputHelper);
            var output = new StringBuilder();
            for (int i = 0; i < iterations; i++)
            {
                output.AppendLine(subject.FormatMessage(pattern, args[i]));
            }

            TestHelpers.Benchmark.End(this.outputHelper);
            this.outputHelper.WriteLine(output.ToString());
        }
 public void DescribeTo(Func<string, SpecInfo> func, MessageFormatter formatter)
 {
     func(_description ?? formatter.FormatMessage(_message));
 }
        public void FormatMessage_with_reflection_overload()
        {
            var subject = new MessageFormatter(false);
            const string Pattern = "You have {UnreadCount, plural, "
                                   + "zero {no unread messages}"
                                   + "one {just one unread message}" + "other {# unread messages}" + "} today.";
            var actual = subject.FormatMessage(Pattern, new { UnreadCount = 0 });
            Assert.Equal("You have no unread messages today.", actual);

            // The absence of UnreadCount should make it throw.
            var ex = Assert.Throws<VariableNotFoundException>(() => subject.FormatMessage(Pattern, new { }));
            Assert.Equal("UnreadCount", ex.MissingVariable);

            actual = subject.FormatMessage(Pattern, new { UnreadCount = 1 });
            Assert.Equal("You have just one unread message today.", actual);
            actual = subject.FormatMessage(Pattern, new { UnreadCount = 2 });
            Assert.Equal("You have 2 unread messages today.", actual);

            actual = subject.FormatMessage(Pattern, new { UnreadCount = 3 });
            Assert.Equal("You have 3 unread messages today.", actual);
        }
 /// <summary>
 /// Persists message <paramref name="message"/>.
 /// </summary>
 /// <param name="message">Message to be persisted.</param>
 public override void Persist(IMessage message)
 {
     Debug.WriteLine(MessageFormatter.FormatMessage(message, FormatString));
 }