예제 #1
0
        public void Pattern_CreatesConstraintFromString()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { d = "foo", };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.Constraints.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("d", kvp.Key);
                var regex = Assert.IsType <RegexRouteConstraint>(Assert.Single(kvp.Value).Constraint);
                Assert.Equal("^(foo)$", regex.Constraint.ToString());
            });
        }
예제 #2
0
        public void Pattern_ExtraConstraints()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { d = new RegexRouteConstraint("foo"), e = new RegexRouteConstraint("bar") };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.Constraints.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("d", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.IsType <RegexRouteConstraint>(c.Constraint));
            },
                kvp =>
            {
                Assert.Equal("e", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.IsType <RegexRouteConstraint>(c.Constraint));
            });
        }
예제 #3
0
        public void Pattern_ExtraConstraints_MatchProcessor()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { d = Mock.Of <MatchProcessor>(), e = Mock.Of <MatchProcessor>(), };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.Constraints.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("d", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.NotNull(c.MatchProcessor));
            },
                kvp =>
            {
                Assert.Equal("e", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.NotNull(c.MatchProcessor));
            });
        }
예제 #4
0
        public void Pattern_RawTextAndArrayOfSegments_ShouldMakeCopyOfArrayOfSegments()
        {
            // Arrange
            var rawText      = "raw";
            var literalPartA = RoutePatternFactory.LiteralPart("A");
            var paramPartB   = RoutePatternFactory.ParameterPart("B");
            var paramPartC   = RoutePatternFactory.ParameterPart("C");
            var paramPartD   = RoutePatternFactory.ParameterPart("D");
            var segments     = new[]
            {
                RoutePatternFactory.Segment(literalPartA, paramPartB),
                RoutePatternFactory.Segment(paramPartC, literalPartA),
                RoutePatternFactory.Segment(paramPartD),
                RoutePatternFactory.Segment(literalPartA)
            };

            // Act
            var actual = RoutePatternFactory.Pattern(rawText, segments);

            segments[1] = RoutePatternFactory.Segment(RoutePatternFactory.ParameterPart("E"));
            Array.Resize(ref segments, 2);

            // Assert
            Assert.Equal(3, actual.Parameters.Count);
            Assert.Same(paramPartB, actual.Parameters[0]);
            Assert.Same(paramPartC, actual.Parameters[1]);
            Assert.Same(paramPartD, actual.Parameters[2]);
        }
예제 #5
0
        public void Pattern_MergesDefaultValues()
        {
            // Arrange
            var template    = "{a}/{b}/{c=19}";
            var defaults    = new { a = "15", b = 17 };
            var constraints = new { };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Equal("15", actual.GetParameter("a").Default);
            Assert.Equal(17, actual.GetParameter("b").Default);
            Assert.Equal("19", actual.GetParameter("c").Default);

            Assert.Collection(
                actual.Defaults.OrderBy(kvp => kvp.Key),
                kvp => { Assert.Equal("a", kvp.Key); Assert.Equal("15", kvp.Value); },
                kvp => { Assert.Equal("b", kvp.Key); Assert.Equal(17, kvp.Value); },
                kvp => { Assert.Equal("c", kvp.Key); Assert.Equal("19", kvp.Value); });
        }
예제 #6
0
        public void Pattern_ExtraConstraints_MultipleConstraintsForKey()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { d = new object[] { new RegexRouteConstraint("foo"), new RegexRouteConstraint("bar"), "baz" } };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.ParameterPolicies.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("d", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.Equal("foo", Assert.IsType <RegexRouteConstraint>(c.ParameterPolicy).Constraint.ToString()),
                    c => Assert.Equal("bar", Assert.IsType <RegexRouteConstraint>(c.ParameterPolicy).Constraint.ToString()),
                    c => Assert.Equal("^(baz)$", Assert.IsType <RegexRouteConstraint>(c.ParameterPolicy).Constraint.ToString()));
            });
        }
예제 #7
0
        public static RoutePattern Parse(string pattern)
        {
            if (pattern == null)
            {
                throw new ArgumentNullException(nameof(pattern));
            }

            var trimmedPattern = TrimPrefix(pattern);

            var context  = new Context(trimmedPattern);
            var segments = new List <RoutePatternPathSegment>();

            while (context.MoveNext())
            {
                var i = context.Index;

                if (context.Current == Separator)
                {
                    // If we get here is means that there's a consecutive '/' character.
                    // Templates don't start with a '/' and parsing a segment consumes the separator.
                    throw new RoutePatternException(pattern, Resources.TemplateRoute_CannotHaveConsecutiveSeparators);
                }

                if (!ParseSegment(context, segments))
                {
                    throw new RoutePatternException(pattern, context.Error);
                }

                // A successful parse should always result in us being at the end or at a separator.
                Debug.Assert(context.AtEnd() || context.Current == Separator);

                if (context.Index <= i)
                {
                    // This shouldn't happen, but we want to crash if it does.
                    var message = "Infinite loop detected in the parser. Please open an issue.";
                    throw new InvalidProgramException(message);
                }
            }

            if (IsAllValid(context, segments))
            {
                return(RoutePatternFactory.Pattern(pattern, segments));
            }
            else
            {
                throw new RoutePatternException(pattern, context.Error);
            }
        }
예제 #8
0
        public void Pattern_MergesConstraints()
        {
            // Arrange
            var template    = "{a:int}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { a = new RegexRouteConstraint("foo"), b = new RegexRouteConstraint("bar") };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.GetParameter("a").Constraints,
                c => Assert.IsType <RegexRouteConstraint>(c.Constraint),
                c => Assert.Equal("int", c.Content));
            Assert.Collection(
                actual.GetParameter("b").Constraints,
                c => Assert.IsType <RegexRouteConstraint>(c.Constraint));

            Assert.Collection(
                actual.Constraints.OrderBy(kvp => kvp.Key),
                kvp =>
            {
                Assert.Equal("a", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.IsType <RegexRouteConstraint>(c.Constraint),
                    c => Assert.Equal("int", c.Content));
            },
                kvp =>
            {
                Assert.Equal("b", kvp.Key);
                Assert.Collection(
                    kvp.Value,
                    c => Assert.IsType <RegexRouteConstraint>(c.Constraint));
            });
        }
예제 #9
0
        public void Pattern_ExtraConstraints_NestedArray_Throws()
        {
            // Arrange
            var template    = "{a}/{b}/{c:int}";
            var defaults    = new { };
            var constraints = new { c = new object[] { new object[0] } };

            var original = RoutePatternFactory.Parse(template);

            // Act & Assert
            Assert.Throws <InvalidOperationException>(() =>
            {
                RoutePatternFactory.Pattern(
                    original.RawText,
                    defaults,
                    constraints,
                    original.PathSegments);
            });
        }
예제 #10
0
        public void Pattern_OptionalParameterDefaultValue_Throws()
        {
            // Arrange
            var template    = "{a}/{b}/{c?}";
            var defaults    = new { c = "15", };
            var constraints = new { };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var ex = Assert.Throws <InvalidOperationException>(() => RoutePatternFactory.Pattern(
                                                                   original.RawText,
                                                                   defaults,
                                                                   constraints,
                                                                   original.PathSegments));

            // Assert
            Assert.Equal(
                "An optional parameter cannot have default value.",
                ex.Message);
        }
예제 #11
0
        public void Pattern_InvalidConstraintTypeThrows()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { };
            var constraints = new { d = 17, };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var ex = Assert.Throws <InvalidOperationException>(() => RoutePatternFactory.Pattern(
                                                                   original.RawText,
                                                                   defaults,
                                                                   constraints,
                                                                   original.PathSegments));

            // Assert
            Assert.Equal(
                $"Invalid constraint '17'. A constraint must be of type 'string', '{typeof(IRouteConstraint)}', or '{typeof(MatchProcessor)}'.",
                ex.Message);
        }
예제 #12
0
        public void Pattern_SameDuplicateDefaultValue()
        {
            // Arrange
            var template    = "{a=13}/{b}/{c}";
            var defaults    = new { a = "13", };
            var constraints = new { };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.Defaults,
                kvp => { Assert.Equal("a", kvp.Key); Assert.Equal("13", kvp.Value); });
        }
예제 #13
0
        public void Pattern_ExtraDefaultValues()
        {
            // Arrange
            var template    = "{a}/{b}/{c}";
            var defaults    = new { d = "15", e = 17 };
            var constraints = new { };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var actual = RoutePatternFactory.Pattern(
                original.RawText,
                defaults,
                constraints,
                original.PathSegments);

            // Assert
            Assert.Collection(
                actual.Defaults.OrderBy(kvp => kvp.Key),
                kvp => { Assert.Equal("d", kvp.Key); Assert.Equal("15", kvp.Value); },
                kvp => { Assert.Equal("e", kvp.Key); Assert.Equal(17, kvp.Value); });
        }
예제 #14
0
        public void Pattern_RawTextAndDefaultsAndParameterPoliciesAndArrayOfSegments_ShouldMakeCopyOfArrayOfSegments()
        {
            // Arrange
            var    rawText           = "raw";
            object defaults          = new { B = 12, C = 4 };
            object parameterPolicies = null;
            var    literalPartA      = RoutePatternFactory.LiteralPart("A");
            var    paramPartB        = RoutePatternFactory.ParameterPart("B");
            var    paramPartC        = RoutePatternFactory.ParameterPart("C");
            var    paramPartD        = RoutePatternFactory.ParameterPart("D");
            var    segments          = new[]
            {
                RoutePatternFactory.Segment(literalPartA, paramPartB),
                RoutePatternFactory.Segment(paramPartC, literalPartA),
                RoutePatternFactory.Segment(paramPartD),
                RoutePatternFactory.Segment(literalPartA)
            };

            // Act
            var actual = RoutePatternFactory.Pattern(rawText, defaults, parameterPolicies, segments);

            segments[1] = RoutePatternFactory.Segment(RoutePatternFactory.ParameterPart("E"));
            Array.Resize(ref segments, 2);

            // Assert
            Assert.Equal(3, actual.Parameters.Count);
            Assert.Equal(paramPartB.Name, actual.Parameters[0].Name);
            Assert.Equal(12, actual.Parameters[0].Default);
            Assert.Null(paramPartB.Default);
            Assert.NotSame(paramPartB, actual.Parameters[0]);
            Assert.Equal(paramPartC.Name, actual.Parameters[1].Name);
            Assert.Equal(4, actual.Parameters[1].Default);
            Assert.NotSame(paramPartC, actual.Parameters[1]);
            Assert.Null(paramPartC.Default);
            Assert.Equal(paramPartD.Name, actual.Parameters[2].Name);
            Assert.Null(actual.Parameters[2].Default);
            Assert.Same(paramPartD, actual.Parameters[2]);
            Assert.Null(paramPartD.Default);
        }
예제 #15
0
        public void Pattern_DuplicateDefaultValue_Throws()
        {
            // Arrange
            var template    = "{a=13}/{b}/{c}";
            var defaults    = new { a = "15", };
            var constraints = new { };

            var original = RoutePatternFactory.Parse(template);

            // Act
            var ex = Assert.Throws <InvalidOperationException>(() => RoutePatternFactory.Pattern(
                                                                   original.RawText,
                                                                   defaults,
                                                                   constraints,
                                                                   original.PathSegments));

            // Assert
            Assert.Equal(
                "The route parameter 'a' has both an inline default value and an explicit default " +
                "value specified. A route parameter cannot contain an inline default value when a " +
                "default value is specified explicitly. Consider removing one of them.",
                ex.Message);
        }