<commit_before>﻿
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using Xunit.Extensions;

namespace Microsoft.AspNet.Routing.Template.Tests
{
    public class TemplateBinderTests
    {
        public static IEnumerable<object[]> EmptyAndNullDefaultValues
        {
            get
            {
                return new object[][]
                {
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val2 = "SomeVal2"}), 
                        null,
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val1 = "a"}), 
                        "Test/a"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val3 = ""}),
                        new RouteValueDictionary(new {val2 = "a"}), 
                        null
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b"}), 
                        "Test/a/b"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b", val3 = "c"}), 
                        "Test/a/b/c"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b"}), 
                        "Test/a/b"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a"}), 
                        "Test/a"
                    },
                    new object[]
                    {
                        "Test/{val1}", 
                        new RouteValueDictionary(new {val1 = "42", val2 = "", val3 = ""}),
                        new RouteValueDictionary(), 
                        "Test"
                    },
                    new object[]
                    {
                       "Test/{val1}/{val2}/{val3}",
                       new RouteValueDictionary(new {val1 = "42", val2 = (string)null, val3 = (string)null}),
                       new RouteValueDictionary(), 
                       "Test"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11", val3 = "", val4 = ""}), 
                        "Test/42/11"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "42"}), 
                        "Test/42"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11"}), 
                        "Test/42/11"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}",
                        new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null}),
                        new RouteValueDictionary(new {val1 = "42"}), 
                        "Test/42"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null, val4 = (string)null}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11"}), 
                        "Test/42/11"
                    },
                };
            }
        }

        [Theory]
        [PropertyData("EmptyAndNullDefaultValues")]
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            IDictionary<string, object> defaults,
            IDictionary<string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(TemplateParser.Parse(template));

            // Act
            var boundTemplate = binder.Bind(defaults, null, values);

            // Assert
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnBothEndsMatches()
        {
            RunTest(
                "language/{lang}-{region}",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/xx-yy");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnLeftEndMatches()
        {
            RunTest(
                "language/{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/xx-yya");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnRightEndMatches()
        {
            RunTest(
                "language/a{lang}-{region}",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/axx-yy");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndMatches()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/axx-yya");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndDoesNotMatch()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "", region = "yy" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndDoesNotMatch2()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnBothEndsMatches()
        {
            RunTest(
                "language/{lang}",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/xx");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnLeftEndMatches()
        {
            RunTest(
                "language/{lang}-",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/xx-");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnRightEndMatches()
        {
            RunTest(
                "language/a{lang}",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/axx");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnNeitherEndMatches()
        {
            RunTest(
                "language/a{lang}a",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/axxa");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentStandardMvcRouteMatches()
        {
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { action = "Index", id = (string)null }),
                new RouteValueDictionary(new { controller = "home", action = "list", id = (string)null }),
                new RouteValueDictionary(new { controller = "products" }),
                "products.mvc");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnBothEndsWithDefaultValuesMatches()
        {
            RunTest(
                "language/{lang}-{region}",
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "zz" }),
                "language/zz-yy");
        }

        [Fact]
        public void GetUrlWithDefaultValue()
        {
            // URL should be found but excluding the 'id' parameter, which has only a default value.
            RunTest(
               "{controller}/{action}/{id}",
               new RouteValueDictionary(new { id = "defaultid" }),
               new RouteValueDictionary(new { controller = "home", action = "oldaction" }),
               new RouteValueDictionary(new { action = "newaction" }),
               "home/newaction");
        }

        [Fact]
        public void GetVirtualPathWithEmptyStringRequiredValueReturnsNull()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = "" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithNullRequiredValueReturnsNull()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = (string)null }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithRequiredValueReturnsPath()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = "home" }),
                "foo/home");
        }

        [Fact]
        public void GetUrlWithNullDefaultValue()
        {
            // URL should be found but excluding the 'id' parameter, which has only a default value.
            RunTest(
                "{controller}/{action}/{id}",
                new RouteValueDictionary(new { id = (string)null }),
                new RouteValueDictionary(new { controller = "home", action = "oldaction", id = (string)null }),
                new RouteValueDictionary(new { action = "newaction" }),
                "home/newaction");
        }

        [Fact]
        public void GetVirtualPathCanFillInSeparatedParametersWithDefaultValues()
        {
            RunTest(
                "{controller}/{language}-{locale}",
                new RouteValueDictionary(new { language = "en", locale = "US" }),
                new RouteValueDictionary(),
                new RouteValueDictionary(new { controller = "Orders" }),
                "Orders/en-US");
        }

        [Fact]
        public void GetVirtualPathWithUnusedNullValueShouldGenerateUrlAndIgnoreNullValue()
        {
            // DevDiv Bugs 194371: UrlRouting: Exception thrown when generating URL that has some null values
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { action = "Index", id = "" }),
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),
                new RouteValueDictionary(new { controller = "Home", action = "TestAction", id = "1", format = (string)null }),
                "Home.mvc/TestAction/1");
        }

        [Fact]
        public void GetUrlWithMissingValuesDoesntMatch()
        {
            RunTest(
                "{controller}/{action}/{id}",
                null,
                new { controller = "home", action = "oldaction" },
                new { action = "newaction" },
                null);
        }

        [Fact]
        public void GetUrlWithEmptyRequiredValuesReturnsNull()
        {
            RunTest(
                "{p1}/{p2}/{p3}",
                null,
                new { p1 = "v1", },
                new { p2 = "", p3 = "" },
                null);
        }

        [Fact]
        public void GetUrlWithEmptyOptionalValuesReturnsShortUrl()
        {
            RunTest(
                "{p1}/{p2}/{p3}",
                new { p2 = "d2", p3 = "d3" },
                new { p1 = "v1", },
                new { p2 = "", p3 = "" },
                "v1");
        }

        [Fact]
        public void GetUrlShouldIgnoreValuesAfterChangedParameter()
        {
            // DevDiv Bugs 157535
            RunTest(
                "{controller}/{action}/{id}",
                new { action = "Index", id = (string)null },
                new { controller = "orig", action = "init", id = "123" },
                new { action = "new", },
                "orig/new");
        }

        [Fact]
        public void GetUrlWithNullForMiddleParameterIgnoresRemainingParameters()
        {
            // DevDiv Bugs 170859: UrlRouting: Passing null or empty string for a parameter in the middle of a route generates the wrong Url
            RunTest(
                "UrlGeneration1/{controller}.mvc/{action}/{category}/{year}/{occasion}/{SafeParam}",
                new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" },
                new { controller = "UrlRouting", action = "Play", category = "Photos", year = "2008", occasion = "Easter", SafeParam = "SafeParamValue" },
                new { year = (string)null, occasion = "Hola" },
                "UrlGeneration1/UrlRouting.mvc/Play/Photos/1995/Hola");
        }

        [Fact]
        public void GetUrlWithEmptyStringForMiddleParameterIgnoresRemainingParameters()
        {
            // DevDiv Bugs 170859: UrlRouting: Passing null or empty string for a parameter in the middle of a route generates the wrong Url
            var ambientValues = new RouteValueDictionary();
            ambientValues.Add("controller", "UrlRouting");
            ambientValues.Add("action", "Play");
            ambientValues.Add("category", "Photos");
            ambientValues.Add("year", "2008");
            ambientValues.Add("occasion", "Easter");
            ambientValues.Add("SafeParam", "SafeParamValue");

            var values = new RouteValueDictionary();
            values.Add("year", String.Empty);
            values.Add("occasion", "Hola");

            RunTest(
                "UrlGeneration1/{controller}.mvc/{action}/{category}/{year}/{occasion}/{SafeParam}",
                new RouteValueDictionary(new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" }),
                ambientValues,
                values,
                "UrlGeneration1/UrlRouting.mvc/Play/Photos/1995/Hola");
        }

        [Fact]
        public void GetUrlWithEmptyStringForMiddleParameterShouldUseDefaultValue()
        {
            // DevDiv Bugs 172084: UrlRouting: Route.BindPath generates the wrong route of new values has a different controller and route has an action parameter with default
            var ambientValues = new RouteValueDictionary();
            ambientValues.Add("Controller", "Test");
            ambientValues.Add("Action", "Fallback");
            ambientValues.Add("param1", "fallback1");
            ambientValues.Add("param2", "fallback2");
            ambientValues.Add("param3", "fallback3");

            var values = new RouteValueDictionary();
            values.Add("controller", "subtest");
            values.Add("param1", "b");
            // The original bug for this included this value, but with the new support for
            // creating query string values it changes the behavior such that the URL is
            // not what was originally expected. To preserve the general behavior of this
            // unit test the 'param2' value is no longer being added.
            //values.Add("param2", "a");

            RunTest(
                "{controller}.mvc/{action}/{param1}",
                new RouteValueDictionary(new { action = "Default" }),
                ambientValues,
                values,
                "subtest.mvc/Default/b");
        }

        [Fact]
        public void GetUrlVerifyEncoding()
        {
            var values = new RouteValueDictionary();
            values.Add("controller", "#;?:@&=+$,");
            values.Add("action", "showcategory");
            values.Add("id", 123);
            values.Add("so?rt", "de?sc");
            values.Add("maxPrice", 100);

            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
                values,
                "%23%3b%3f%3a%40%26%3d%2b%24%2c.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100");
        }

        [Fact]
        public void GetUrlGeneratesQueryStringForNewValuesAndEscapesQueryString()
        {
            var values = new RouteValueDictionary(new { controller = "products", action = "showcategory", id = 123, maxPrice = 100 });
            values.Add("so?rt", "de?sc");

            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
                values,
               "products.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100");
        }

        [Fact]
        public void GetUrlGeneratesQueryStringForNewValuesButIgnoresNewValuesThatMatchDefaults()
        {
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home", Custom = "customValue" }),
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = (string)null }),
                new RouteValueDictionary(new { controller = "products", action = "showcategory", id = 123, sort = "desc", maxPrice = 100, custom = "customValue" }),
                "products.mvc/showcategory/123?sort=desc&maxPrice=100");
        }

        [Fact]
        public void GetVirtualPathEncodesParametersAndLiterals()
        {
            RunTest(
                "bl%og/{controller}/he llo/{action}",
                null,
                new RouteValueDictionary(new { controller = "ho%me", action = "li st" }),
                new RouteValueDictionary(),
                "bl%25og/ho%25me/he%20llo/li%20st");
        }

        [Fact]
        public void GetUrlWithCatchAllWithValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = "v2a/v2b" }),
                "v1/v2a/v2b");
        }

        [Fact]
        public void GetUrlWithCatchAllWithEmptyValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = "" }),
                "v1");
        }

        [Fact]
        public void GetUrlWithCatchAllWithNullValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = (string)null }),
                "v1");
        }

#if ROUTE_COLLECTION

                [Fact]
        public void GetUrlShouldValidateOnlyAcceptedParametersAndUserDefaultValuesForInvalidatedParameters()
        {
            // DevDiv Bugs 172913: UrlRouting: Parameter validation should not run against current request values if a new value has been supplied at a previous position

            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("Controller", "UrlRouting");
            rd.Values.Add("Name", "MissmatchedValidateParams");
            rd.Values.Add("action", "MissmatchedValidateParameters2");
            rd.Values.Add("ValidateParam1", "special1");
            rd.Values.Add("ValidateParam2", "special2");

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "UrlConstraints/Validation.mvc/Input5/{action}/{ValidateParam1}/{ValidateParam2}",
                new RouteValueDictionary(new { Controller = "UrlRouting", Name = "MissmatchedValidateParams", ValidateParam2 = "valid" }),
                new RouteValueDictionary(new { ValidateParam1 = "valid.*", ValidateParam2 = "valid.*" })));

            rc.Add(CreateRoute(
                "UrlConstraints/Validation.mvc/Input5/{action}/{ValidateParam1}/{ValidateParam2}",
                new RouteValueDictionary(new { Controller = "UrlRouting", Name = "MissmatchedValidateParams" }),
                new RouteValueDictionary(new { ValidateParam1 = "special.*", ValidateParam2 = "special.*" })));

            var values = CreateRouteValueDictionary();
            values.Add("Name", "MissmatchedValidateParams");
            values.Add("ValidateParam1", "valid1");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/UrlConstraints/Validation.mvc/Input5/MissmatchedValidateParameters2/valid1", vpd.VirtualPath);
        }

               [Fact]
        public void GetUrlWithRouteThatHasExtensionWithSubsequentDefaultValueIncludesExtensionButNotDefaultValue()
        {
            // DevDiv Bugs 156606

            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bank");
            rd.Values.Add("action", "MakeDeposit");
            rd.Values.Add("accountId", "7770");

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "{controller}.mvc/Deposit/{accountId}",
                new RouteValueDictionary(new { Action = "DepositView" })));

            // Note: This route was in the original bug, but it turns out that this behavior is incorrect. With the
            // recent fix to Route (in this changelist) this route would have been selected since we have values for
            // all three required parameters.
            //rc.Add(new Route {
            //    Url = "{controller}.mvc/{action}/{accountId}",
            //    RouteHandler = new DummyRouteHandler()
            //});

            // This route should be chosen because the requested action is List. Since the default value of the action
            // is List then the Action should not be in the URL. However, the file extension should be included since
            // it is considered "safe."
            rc.Add(CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { Action = "List" })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "List");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Bank.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithRouteThatHasDifferentControllerCaseShouldStillMatch()
        {
            // DevDiv Bugs 159099

            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bar");
            rd.Values.Add("action", "bbb");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute("PrettyFooUrl", new RouteValueDictionary(new { controller = "Foo", action = "aaa", id = (string)null })));

            rc.Add(CreateRoute("PrettyBarUrl", new RouteValueDictionary(new { controller = "Bar", action = "bbb", id = (string)null })));

            rc.Add(CreateRoute("{controller}/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "aaa");
            values.Add("Controller", "foo");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/PrettyFooUrl", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithNoChangedValuesShouldProduceSameUrl()
        {
            // DevDiv Bugs 159469

            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Home");
            rd.Values.Add("action", "Index");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute("{controller}.mvc/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            rc.Add(CreateRoute("{controller}/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "Index");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Home.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlAppliesConstraintsRulesToChooseRoute()
        {
            // DevDiv Bugs 159678: MVC: URL generation chooses the wrong route for generating URLs when route validation is in place

            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Home");
            rd.Values.Add("action", "Index");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "foo.mvc/{action}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "Home", action = "Contact", httpMethod = CreateHttpMethodConstraint("get") })));

            rc.Add(CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { action = "Index" }),
                new RouteValueDictionary(new { controller = "Home", action = "(Index|About)", httpMethod = CreateHttpMethodConstraint("post") })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "Index");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Home.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithValuesThatAreCompletelyDifferentFromTheCurrentRoute()
        {
            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);
            IRouteCollection rt = new DefaultRouteCollection();
            rt.Add(CreateRoute("date/{y}/{m}/{d}", null));
            rt.Add(CreateRoute("{controller}/{action}/{id}", null));

            var rd = CreateRouteData();
            rd.Values.Add("controller", "home");
            rd.Values.Add("action", "dostuff");

            var values = CreateRouteValueDictionary();
            values.Add("y", "2007");
            values.Add("m", "08");
            values.Add("d", "12");

            // Act
            var vpd = rt.GetVirtualPath(context, values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app/date/2007/08/12", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithValuesThatAreCompletelyDifferentFromTheCurrentRouteAsSecondRoute()
        {
            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);

            IRouteCollection rt = new DefaultRouteCollection();
            rt.Add(CreateRoute("{controller}/{action}/{id}"));
            rt.Add(CreateRoute("date/{y}/{m}/{d}"));

            var rd = CreateRouteData();
            rd.Values.Add("controller", "home");
            rd.Values.Add("action", "dostuff");

            var values = CreateRouteValueDictionary();
            values.Add("y", "2007");
            values.Add("m", "08");
            values.Add("d", "12");

            // Act
            var vpd = rt.GetVirtualPath(context, values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app/date/2007/08/12", vpd.VirtualPath);
        }

        [Fact]
        public void GetVirtualPathUsesCurrentValuesNotInRouteToMatch()
        {
            // DevDiv Bugs 177401: UrlRouting: Incorrect route picked on urlgeneration if using controller from ambient values and route does not have a url parameter for controller

            // DevDiv Bugs 191162: UrlRouting: Route does not match when an ambient route value doesn't match a required default value in the target route
            // Because of this bug the test was split into two separate verifications since the original test was verifying slightly incorrect behavior

            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);
            TemplateRoute r1 = CreateRoute(
                "ParameterMatching.mvc/{Action}/{product}",
                new RouteValueDictionary(new { Controller = "ParameterMatching", product = (string)null }),
                null);

            TemplateRoute r2 = CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { Action = "List" }),
                new RouteValueDictionary(new { Controller = "Action|Bank|Overridden|DerivedFromAction|OverrideInvokeActionAndExecute|InvalidControllerName|Store|HtmlHelpers|(T|t)est|UrlHelpers|Custom|Parent|Child|TempData|ViewFactory|LocatingViews|AccessingDataInViews|ViewOverrides|ViewMasterPage|InlineCompileError|CustomView" }),
                null);

            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bank");
            rd.Values.Add("Action", "List");
            var valuesDictionary = CreateRouteValueDictionary();
            valuesDictionary.Add("action", "AttemptLogin");

            // Act for first route
            var vpd = r1.GetVirtualPath(context, valuesDictionary);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("ParameterMatching.mvc/AttemptLogin", vpd.VirtualPath);

            // Act for second route<commit_msg>Removing old comments about ancient bug numbers<commit_after>﻿
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using Xunit.Extensions;

namespace Microsoft.AspNet.Routing.Template.Tests
{
    public class TemplateBinderTests
    {
        public static IEnumerable<object[]> EmptyAndNullDefaultValues
        {
            get
            {
                return new object[][]
                {
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val2 = "SomeVal2"}), 
                        null,
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val1 = "a"}), 
                        "Test/a"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val3 = ""}),
                        new RouteValueDictionary(new {val2 = "a"}), 
                        null
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}", 
                        new RouteValueDictionary(new {val1 = "", val2 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b"}), 
                        "Test/a/b"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b", val3 = "c"}), 
                        "Test/a/b/c"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a", val2 = "b"}), 
                        "Test/a/b"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "a"}), 
                        "Test/a"
                    },
                    new object[]
                    {
                        "Test/{val1}", 
                        new RouteValueDictionary(new {val1 = "42", val2 = "", val3 = ""}),
                        new RouteValueDictionary(), 
                        "Test"
                    },
                    new object[]
                    {
                       "Test/{val1}/{val2}/{val3}",
                       new RouteValueDictionary(new {val1 = "42", val2 = (string)null, val3 = (string)null}),
                       new RouteValueDictionary(), 
                       "Test"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11", val3 = "", val4 = ""}), 
                        "Test/42/11"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}", 
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = ""}),
                        new RouteValueDictionary(new {val1 = "42"}), 
                        "Test/42"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = "", val3 = "", val4 = ""}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11"}), 
                        "Test/42/11"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}",
                        new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null}),
                        new RouteValueDictionary(new {val1 = "42"}), 
                        "Test/42"
                    },
                    new object[]
                    {
                        "Test/{val1}/{val2}/{val3}/{val4}",
                        new RouteValueDictionary(new {val1 = "21", val2 = (string)null, val3 = (string)null, val4 = (string)null}),
                        new RouteValueDictionary(new {val1 = "42", val2 = "11"}), 
                        "Test/42/11"
                    },
                };
            }
        }

        [Theory]
        [PropertyData("EmptyAndNullDefaultValues")]
        public void Binding_WithEmptyAndNull_DefaultValues(
            string template,
            IDictionary<string, object> defaults,
            IDictionary<string, object> values,
            string expected)
        {
            // Arrange
            var binder = new TemplateBinder(TemplateParser.Parse(template));

            // Act
            var boundTemplate = binder.Bind(defaults, null, values);

            // Assert
            if (expected == null)
            {
                Assert.Null(boundTemplate);
            }
            else
            {
                Assert.NotNull(boundTemplate);
                Assert.Equal(expected, boundTemplate);
            }
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnBothEndsMatches()
        {
            RunTest(
                "language/{lang}-{region}",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/xx-yy");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnLeftEndMatches()
        {
            RunTest(
                "language/{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/xx-yya");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnRightEndMatches()
        {
            RunTest(
                "language/a{lang}-{region}",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/axx-yy");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndMatches()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                "language/axx-yya");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndDoesNotMatch()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "", region = "yy" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnNeitherEndDoesNotMatch2()
        {
            RunTest(
                "language/a{lang}-{region}a",
                null,
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "xx", region = "" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnBothEndsMatches()
        {
            RunTest(
                "language/{lang}",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/xx");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnLeftEndMatches()
        {
            RunTest(
                "language/{lang}-",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/xx-");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnRightEndMatches()
        {
            RunTest(
                "language/a{lang}",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/axx");
        }

        [Fact]
        public void GetVirtualPathWithSimpleMultiSegmentParamsOnNeitherEndMatches()
        {
            RunTest(
                "language/a{lang}a",
                null,
                new RouteValueDictionary(new { lang = "en" }),
                new RouteValueDictionary(new { lang = "xx" }),
                "language/axxa");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentStandardMvcRouteMatches()
        {
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { action = "Index", id = (string)null }),
                new RouteValueDictionary(new { controller = "home", action = "list", id = (string)null }),
                new RouteValueDictionary(new { controller = "products" }),
                "products.mvc");
        }

        [Fact]
        public void GetVirtualPathWithMultiSegmentParamsOnBothEndsWithDefaultValuesMatches()
        {
            RunTest(
                "language/{lang}-{region}",
                new RouteValueDictionary(new { lang = "xx", region = "yy" }),
                new RouteValueDictionary(new { lang = "en", region = "US" }),
                new RouteValueDictionary(new { lang = "zz" }),
                "language/zz-yy");
        }

        [Fact]
        public void GetUrlWithDefaultValue()
        {
            // URL should be found but excluding the 'id' parameter, which has only a default value.
            RunTest(
               "{controller}/{action}/{id}",
               new RouteValueDictionary(new { id = "defaultid" }),
               new RouteValueDictionary(new { controller = "home", action = "oldaction" }),
               new RouteValueDictionary(new { action = "newaction" }),
               "home/newaction");
        }

        [Fact]
        public void GetVirtualPathWithEmptyStringRequiredValueReturnsNull()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = "" }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithNullRequiredValueReturnsNull()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = (string)null }),
                null);
        }

        [Fact]
        public void GetVirtualPathWithRequiredValueReturnsPath()
        {
            RunTest(
                "foo/{controller}",
                null,
                new RouteValueDictionary(new { }),
                new RouteValueDictionary(new { controller = "home" }),
                "foo/home");
        }

        [Fact]
        public void GetUrlWithNullDefaultValue()
        {
            // URL should be found but excluding the 'id' parameter, which has only a default value.
            RunTest(
                "{controller}/{action}/{id}",
                new RouteValueDictionary(new { id = (string)null }),
                new RouteValueDictionary(new { controller = "home", action = "oldaction", id = (string)null }),
                new RouteValueDictionary(new { action = "newaction" }),
                "home/newaction");
        }

        [Fact]
        public void GetVirtualPathCanFillInSeparatedParametersWithDefaultValues()
        {
            RunTest(
                "{controller}/{language}-{locale}",
                new RouteValueDictionary(new { language = "en", locale = "US" }),
                new RouteValueDictionary(),
                new RouteValueDictionary(new { controller = "Orders" }),
                "Orders/en-US");
        }

        [Fact]
        public void GetVirtualPathWithUnusedNullValueShouldGenerateUrlAndIgnoreNullValue()
        {
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { action = "Index", id = "" }),
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),
                new RouteValueDictionary(new { controller = "Home", action = "TestAction", id = "1", format = (string)null }),
                "Home.mvc/TestAction/1");
        }

        [Fact]
        public void GetUrlWithMissingValuesDoesntMatch()
        {
            RunTest(
                "{controller}/{action}/{id}",
                null,
                new { controller = "home", action = "oldaction" },
                new { action = "newaction" },
                null);
        }

        [Fact]
        public void GetUrlWithEmptyRequiredValuesReturnsNull()
        {
            RunTest(
                "{p1}/{p2}/{p3}",
                null,
                new { p1 = "v1", },
                new { p2 = "", p3 = "" },
                null);
        }

        [Fact]
        public void GetUrlWithEmptyOptionalValuesReturnsShortUrl()
        {
            RunTest(
                "{p1}/{p2}/{p3}",
                new { p2 = "d2", p3 = "d3" },
                new { p1 = "v1", },
                new { p2 = "", p3 = "" },
                "v1");
        }

        [Fact]
        public void GetUrlShouldIgnoreValuesAfterChangedParameter()
        {
            RunTest(
                "{controller}/{action}/{id}",
                new { action = "Index", id = (string)null },
                new { controller = "orig", action = "init", id = "123" },
                new { action = "new", },
                "orig/new");
        }

        [Fact]
        public void GetUrlWithNullForMiddleParameterIgnoresRemainingParameters()
        {
            RunTest(
                "UrlGeneration1/{controller}.mvc/{action}/{category}/{year}/{occasion}/{SafeParam}",
                new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" },
                new { controller = "UrlRouting", action = "Play", category = "Photos", year = "2008", occasion = "Easter", SafeParam = "SafeParamValue" },
                new { year = (string)null, occasion = "Hola" },
                "UrlGeneration1/UrlRouting.mvc/Play/Photos/1995/Hola");
        }

        [Fact]
        public void GetUrlWithEmptyStringForMiddleParameterIgnoresRemainingParameters()
        {
            var ambientValues = new RouteValueDictionary();
            ambientValues.Add("controller", "UrlRouting");
            ambientValues.Add("action", "Play");
            ambientValues.Add("category", "Photos");
            ambientValues.Add("year", "2008");
            ambientValues.Add("occasion", "Easter");
            ambientValues.Add("SafeParam", "SafeParamValue");

            var values = new RouteValueDictionary();
            values.Add("year", String.Empty);
            values.Add("occasion", "Hola");

            RunTest(
                "UrlGeneration1/{controller}.mvc/{action}/{category}/{year}/{occasion}/{SafeParam}",
                new RouteValueDictionary(new { year = 1995, occasion = "Christmas", action = "Play", SafeParam = "SafeParamValue" }),
                ambientValues,
                values,
                "UrlGeneration1/UrlRouting.mvc/Play/Photos/1995/Hola");
        }

        [Fact]
        public void GetUrlWithEmptyStringForMiddleParameterShouldUseDefaultValue()
        {
            var ambientValues = new RouteValueDictionary();
            ambientValues.Add("Controller", "Test");
            ambientValues.Add("Action", "Fallback");
            ambientValues.Add("param1", "fallback1");
            ambientValues.Add("param2", "fallback2");
            ambientValues.Add("param3", "fallback3");

            var values = new RouteValueDictionary();
            values.Add("controller", "subtest");
            values.Add("param1", "b");

            RunTest(
                "{controller}.mvc/{action}/{param1}",
                new RouteValueDictionary(new { action = "Default" }),
                ambientValues,
                values,
                "subtest.mvc/Default/b");
        }

        [Fact]
        public void GetUrlVerifyEncoding()
        {
            var values = new RouteValueDictionary();
            values.Add("controller", "#;?:@&=+$,");
            values.Add("action", "showcategory");
            values.Add("id", 123);
            values.Add("so?rt", "de?sc");
            values.Add("maxPrice", 100);

            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
                values,
                "%23%3b%3f%3a%40%26%3d%2b%24%2c.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100");
        }

        [Fact]
        public void GetUrlGeneratesQueryStringForNewValuesAndEscapesQueryString()
        {
            var values = new RouteValueDictionary(new { controller = "products", action = "showcategory", id = 123, maxPrice = 100 });
            values.Add("so?rt", "de?sc");

            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "home", action = "Index", id = (string)null }),
                values,
               "products.mvc/showcategory/123?so%3Frt=de%3Fsc&maxPrice=100");
        }

        [Fact]
        public void GetUrlGeneratesQueryStringForNewValuesButIgnoresNewValuesThatMatchDefaults()
        {
            RunTest(
                "{controller}.mvc/{action}/{id}",
                new RouteValueDictionary(new { controller = "Home", Custom = "customValue" }),
                new RouteValueDictionary(new { controller = "Home", action = "Index", id = (string)null }),
                new RouteValueDictionary(new { controller = "products", action = "showcategory", id = 123, sort = "desc", maxPrice = 100, custom = "customValue" }),
                "products.mvc/showcategory/123?sort=desc&maxPrice=100");
        }

        [Fact]
        public void GetVirtualPathEncodesParametersAndLiterals()
        {
            RunTest(
                "bl%og/{controller}/he llo/{action}",
                null,
                new RouteValueDictionary(new { controller = "ho%me", action = "li st" }),
                new RouteValueDictionary(),
                "bl%25og/ho%25me/he%20llo/li%20st");
        }

        [Fact]
        public void GetUrlWithCatchAllWithValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = "v2a/v2b" }),
                "v1/v2a/v2b");
        }

        [Fact]
        public void GetUrlWithCatchAllWithEmptyValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = "" }),
                "v1");
        }

        [Fact]
        public void GetUrlWithCatchAllWithNullValue()
        {
            RunTest(
                "{p1}/{*p2}",
                new RouteValueDictionary(new { id = "defaultid" }),
                new RouteValueDictionary(new { p1 = "v1" }),
                new RouteValueDictionary(new { p2 = (string)null }),
                "v1");
        }

#if ROUTE_COLLECTION

                [Fact]
        public void GetUrlShouldValidateOnlyAcceptedParametersAndUserDefaultValuesForInvalidatedParameters()
        {
            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("Controller", "UrlRouting");
            rd.Values.Add("Name", "MissmatchedValidateParams");
            rd.Values.Add("action", "MissmatchedValidateParameters2");
            rd.Values.Add("ValidateParam1", "special1");
            rd.Values.Add("ValidateParam2", "special2");

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "UrlConstraints/Validation.mvc/Input5/{action}/{ValidateParam1}/{ValidateParam2}",
                new RouteValueDictionary(new { Controller = "UrlRouting", Name = "MissmatchedValidateParams", ValidateParam2 = "valid" }),
                new RouteValueDictionary(new { ValidateParam1 = "valid.*", ValidateParam2 = "valid.*" })));

            rc.Add(CreateRoute(
                "UrlConstraints/Validation.mvc/Input5/{action}/{ValidateParam1}/{ValidateParam2}",
                new RouteValueDictionary(new { Controller = "UrlRouting", Name = "MissmatchedValidateParams" }),
                new RouteValueDictionary(new { ValidateParam1 = "special.*", ValidateParam2 = "special.*" })));

            var values = CreateRouteValueDictionary();
            values.Add("Name", "MissmatchedValidateParams");
            values.Add("ValidateParam1", "valid1");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/UrlConstraints/Validation.mvc/Input5/MissmatchedValidateParameters2/valid1", vpd.VirtualPath);
        }

               [Fact]
        public void GetUrlWithRouteThatHasExtensionWithSubsequentDefaultValueIncludesExtensionButNotDefaultValue()
        {
            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bank");
            rd.Values.Add("action", "MakeDeposit");
            rd.Values.Add("accountId", "7770");

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "{controller}.mvc/Deposit/{accountId}",
                new RouteValueDictionary(new { Action = "DepositView" })));

            // Note: This route was in the original bug, but it turns out that this behavior is incorrect. With the
            // recent fix to Route (in this changelist) this route would have been selected since we have values for
            // all three required parameters.
            //rc.Add(new Route {
            //    Url = "{controller}.mvc/{action}/{accountId}",
            //    RouteHandler = new DummyRouteHandler()
            //});

            // This route should be chosen because the requested action is List. Since the default value of the action
            // is List then the Action should not be in the URL. However, the file extension should be included since
            // it is considered "safe."
            rc.Add(CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { Action = "List" })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "List");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Bank.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithRouteThatHasDifferentControllerCaseShouldStillMatch()
        {
            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bar");
            rd.Values.Add("action", "bbb");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute("PrettyFooUrl", new RouteValueDictionary(new { controller = "Foo", action = "aaa", id = (string)null })));

            rc.Add(CreateRoute("PrettyBarUrl", new RouteValueDictionary(new { controller = "Bar", action = "bbb", id = (string)null })));

            rc.Add(CreateRoute("{controller}/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "aaa");
            values.Add("Controller", "foo");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/PrettyFooUrl", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithNoChangedValuesShouldProduceSameUrl()
        {
            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Home");
            rd.Values.Add("action", "Index");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute("{controller}.mvc/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            rc.Add(CreateRoute("{controller}/{action}/{id}", new RouteValueDictionary(new { action = "Index", id = (string)null })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "Index");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Home.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlAppliesConstraintsRulesToChooseRoute()
        {
            // Arrange
            var rd = CreateRouteData();
            rd.Values.Add("controller", "Home");
            rd.Values.Add("action", "Index");
            rd.Values.Add("id", null);

            IRouteCollection rc = new DefaultRouteCollection();
            rc.Add(CreateRoute(
                "foo.mvc/{action}",
                new RouteValueDictionary(new { controller = "Home" }),
                new RouteValueDictionary(new { controller = "Home", action = "Contact", httpMethod = CreateHttpMethodConstraint("get") })));

            rc.Add(CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { action = "Index" }),
                new RouteValueDictionary(new { controller = "Home", action = "(Index|About)", httpMethod = CreateHttpMethodConstraint("post") })));

            var values = CreateRouteValueDictionary();
            values.Add("Action", "Index");

            // Act
            var vpd = rc.GetVirtualPath(GetHttpContext("/app1", "", ""), values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app1/Home.mvc", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithValuesThatAreCompletelyDifferentFromTheCurrentRoute()
        {
            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);
            IRouteCollection rt = new DefaultRouteCollection();
            rt.Add(CreateRoute("date/{y}/{m}/{d}", null));
            rt.Add(CreateRoute("{controller}/{action}/{id}", null));

            var rd = CreateRouteData();
            rd.Values.Add("controller", "home");
            rd.Values.Add("action", "dostuff");

            var values = CreateRouteValueDictionary();
            values.Add("y", "2007");
            values.Add("m", "08");
            values.Add("d", "12");

            // Act
            var vpd = rt.GetVirtualPath(context, values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app/date/2007/08/12", vpd.VirtualPath);
        }

        [Fact]
        public void GetUrlWithValuesThatAreCompletelyDifferentFromTheCurrentRouteAsSecondRoute()
        {
            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);

            IRouteCollection rt = new DefaultRouteCollection();
            rt.Add(CreateRoute("{controller}/{action}/{id}"));
            rt.Add(CreateRoute("date/{y}/{m}/{d}"));

            var rd = CreateRouteData();
            rd.Values.Add("controller", "home");
            rd.Values.Add("action", "dostuff");

            var values = CreateRouteValueDictionary();
            values.Add("y", "2007");
            values.Add("m", "08");
            values.Add("d", "12");

            // Act
            var vpd = rt.GetVirtualPath(context, values);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("/app/date/2007/08/12", vpd.VirtualPath);
        }

        [Fact]
        public void GetVirtualPathUsesCurrentValuesNotInRouteToMatch()
        {
            // Arrange
            HttpContext context = GetHttpContext("/app", null, null);
            TemplateRoute r1 = CreateRoute(
                "ParameterMatching.mvc/{Action}/{product}",
                new RouteValueDictionary(new { Controller = "ParameterMatching", product = (string)null }),
                null);

            TemplateRoute r2 = CreateRoute(
                "{controller}.mvc/{action}",
                new RouteValueDictionary(new { Action = "List" }),
                new RouteValueDictionary(new { Controller = "Action|Bank|Overridden|DerivedFromAction|OverrideInvokeActionAndExecute|InvalidControllerName|Store|HtmlHelpers|(T|t)est|UrlHelpers|Custom|Parent|Child|TempData|ViewFactory|LocatingViews|AccessingDataInViews|ViewOverrides|ViewMasterPage|InlineCompileError|CustomView" }),
                null);

            var rd = CreateRouteData();
            rd.Values.Add("controller", "Bank");
            rd.Values.Add("Action", "List");
            var valuesDictionary = CreateRouteValueDictionary();
            valuesDictionary.Add("action", "AttemptLogin");

            // Act for first route
            var vpd = r1.GetVirtualPath(context, valuesDictionary);

            // Assert
            Assert.NotNull(vpd);
            Assert.Equal<string>("ParameterMatching.mvc/AttemptLogin", vpd.VirtualPath);

            // Act for second route
{'fraction_non_alphanumeric': 0.12380567053624225, 'fraction_numerical': 0.012480159347670473, 'mean_word_length': 1.590205562273277, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4230701', 'n_tokens_mistral': 17009, 'n_tokens_neox': 15991, 'n_words': 3324}