< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.UriTemplateHelpers
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplateHelpers.cs
Line coverage
27%
Covered lines: 37
Uncovered lines: 99
Coverable lines: 136
Total lines: 410
Line coverage: 27.2%
Branch coverage
27%
Covered branches: 33
Total branches: 118
Branch coverage: 27.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
AssertCanonical(...)100%110%
CanMatchQueryInterestingly(...)36.36%222231.81%
CanMatchQueryTrivially(...)100%11100%
DisambiguateSamePath(...)0%16160%
GetQueryKeyComparer()100%110%
GetUriPath(...)100%11100%
HasQueryLiteralRequirements(...)75%4460%
IdentifyPartType(...)78.57%141468.75%
IsWildcardPath(...)100%22100%
IsWildcardSegment(...)87.5%8887.5%
ParseQueryString(...)50%4450%
AllTemplatesAreEquivalent(...)0%440%
EnsureQueriesAreDistinct(...)0%34340%
GetQueryLiterals(...)0%440%
Same(...)0%440%
Compare(...)100%110%
Equals(...)100%110%
GetHashCode(...)0%220%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/UriTemplateHelpers.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Collections.Specialized;
 7using System.Diagnostics;
 8using CoreWCF.Runtime;
 9
 10namespace CoreWCF
 11{
 12    internal static class UriTemplateHelpers
 13    {
 014        private static readonly UriTemplateQueryComparer s_queryComparer = new UriTemplateQueryComparer();
 015        private static readonly UriTemplateQueryKeyComparer s_queryKeyComperar = new UriTemplateQueryKeyComparer();
 16
 17        [Conditional("DEBUG")]
 18        public static void AssertCanonical(string s)
 19        {
 20            Fx.Assert(s == s.ToUpperInvariant(), "non-canonicalized");
 021        }
 22
 23        public static bool CanMatchQueryInterestingly(UriTemplate ut, NameValueCollection query, bool mustBeEspeciallyIn
 24        {
 125            if (ut._queries.Count == 0)
 26            {
 027                return false; // trivial, not interesting
 28            }
 29
 130            string[] queryKeys = query.AllKeys;
 431            foreach (KeyValuePair<string, UriTemplateQueryValue> kvp in ut._queries)
 32            {
 133                string queryKeyName = kvp.Key;
 134                if (kvp.Value.Nature == UriTemplatePartType.Literal)
 35                {
 036                    bool queryKeysContainsQueryVarName = false;
 037                    for (int i = 0; i < queryKeys.Length; ++i)
 38                    {
 039                        if (StringComparer.OrdinalIgnoreCase.Equals(queryKeys[i], queryKeyName))
 40                        {
 041                            queryKeysContainsQueryVarName = true;
 042                            break;
 43                        }
 44                    }
 45
 046                    if (!queryKeysContainsQueryVarName)
 47                    {
 048                        return false;
 49                    }
 50
 051                    if (kvp.Value == UriTemplateQueryValue.Empty)
 52                    {
 053                        if (!string.IsNullOrEmpty(query[queryKeyName]))
 54                        {
 055                            return false;
 56                        }
 57                    }
 58                    else
 59                    {
 060                        if (((UriTemplateLiteralQueryValue)(kvp.Value)).AsRawUnescapedString() != query[queryKeyName])
 61                        {
 062                            return false;
 63                        }
 64                    }
 65                }
 66                else
 67                {
 168                    if (mustBeEspeciallyInteresting && Array.IndexOf(queryKeys, queryKeyName) == -1)
 69                    {
 070                        return false;
 71                    }
 72                }
 73            }
 74
 175            return true;
 076        }
 77
 17078        public static bool CanMatchQueryTrivially(UriTemplate ut) => ut._queries.Count == 0;
 79
 80        public static void DisambiguateSamePath(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUriTempl
 81        {
 82            // [a,b) all have same path
 83            // ensure queries make them unambiguous
 84            Fx.Assert(b > a, "array bug");
 85
 86            // sort empty queries to front
 087            Array.Sort(array, a, b - a, s_queryComparer);
 088            if (b - a == 1)
 89            {
 090                return; // if only one, cannot be ambiguous
 91            }
 92
 093            if (!allowDuplicateEquivalentUriTemplates)
 94            {
 95                // ensure at most one empty query and ignore it
 096                if (array[a]._queries.Count == 0)
 97                {
 098                    a++;
 99                }
 100
 0101                if (array[a]._queries.Count == 0)
 102                {
 0103                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
 0104                        SR.UTTDuplicate, array[a].ToString(), array[a - 1].ToString())));
 105                }
 106
 0107                if (b - a == 1)
 108                {
 0109                    return; // if only one, cannot be ambiguous
 110                }
 111            }
 112            else
 113            {
 0114                while (a < b && array[a]._queries.Count == 0)  // all equivalent
 115                {
 0116                    a++;
 117                }
 118
 0119                if (b - a <= 1)
 120                {
 0121                    return;
 122                }
 123            }
 124
 125            Fx.Assert(b > a, "array bug");
 126
 127            // now consider non-empty queries
 128            // more than one, so enforce that
 129            // forall
 130            //   exist set of querystringvars S where
 131            //     every op has literal value foreach var in S, and
 132            //     those literal tuples are different
 0133            EnsureQueriesAreDistinct(array, a, b, allowDuplicateEquivalentUriTemplates);
 0134        }
 135
 0136        public static IEqualityComparer<string> GetQueryKeyComparer() => s_queryKeyComperar;
 137
 198138        public static string GetUriPath(Uri uri) => uri.GetComponents(UriComponents.Path | UriComponents.KeepDelimiter, 
 139
 140        public static bool HasQueryLiteralRequirements(UriTemplate ut)
 141        {
 4142            foreach (UriTemplateQueryValue utqv in ut._queries.Values)
 143            {
 1144                if (utqv.Nature == UriTemplatePartType.Literal)
 145                {
 0146                    return true;
 147                }
 148            }
 149
 1150            return false;
 0151        }
 152
 153        public static UriTemplatePartType IdentifyPartType(string part)
 154        {
 155            // Identifying the nature of a string - Literal|Compound|Variable
 156            // Algorithem is based on the following steps:
 157            // - Finding the position of the first open curlly brace ('{') and close curlly brace ('}')
 158            //    in the string
 159            // - If we don't find any this is a Literal
 160            // - otherwise, we validate that position of the close brace is at least two characters from
 161            //    the position of the open brace
 162            // - Then we identify if we are dealing with a compound string or a single variable string
 163            //    + var name is not at the string start --> Compound
 164            //    + var name is shorter then the entire string (End < Length-2 or End==Length-2
 165            //       and string ends with '/') --> Compound
 166            //    + otherwise --> Variable
 1154167            int varStartIndex = part.IndexOf("{", StringComparison.Ordinal);
 1154168            int varEndIndex = part.IndexOf("}", StringComparison.Ordinal);
 1154169            if (varStartIndex == -1)
 170            {
 951171                if (varEndIndex != -1)
 172                {
 0173                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 0174                        SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, part)));
 175                }
 176
 951177                return UriTemplatePartType.Literal;
 178            }
 179            else
 180            {
 203181                if (varEndIndex < varStartIndex + 2)
 182                {
 0183                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(
 0184                        SR.Format(SR.UTInvalidFormatSegmentOrQueryPart, part)));
 185                }
 186
 203187                if (varStartIndex > 0)
 188                {
 0189                    return UriTemplatePartType.Compound;
 190                }
 203191                else if ((varEndIndex < part.Length - 2) ||
 203192                    ((varEndIndex == part.Length - 2) && !part.EndsWith("/", StringComparison.Ordinal)))
 193                {
 48194                    return UriTemplatePartType.Compound;
 195                }
 196                else
 197                {
 155198                    return UriTemplatePartType.Variable;
 199                }
 200            }
 201        }
 202
 203        public static bool IsWildcardPath(string path)
 204        {
 163205            if (path.IndexOf('/') != -1)
 206            {
 155207                return false;
 208            }
 209
 8210            return IsWildcardSegment(path, out UriTemplatePartType partType);
 211        }
 212
 213        public static bool IsWildcardSegment(string segment, out UriTemplatePartType type)
 214        {
 520215            type = IdentifyPartType(segment);
 520216            switch (type)
 217            {
 218                case UriTemplatePartType.Literal:
 422219                    return (string.Compare(segment, UriTemplate.WildcardPath, StringComparison.Ordinal) == 0);
 220
 221                case UriTemplatePartType.Compound:
 24222                    return false;
 223
 224                case UriTemplatePartType.Variable:
 74225                    return ((segment.IndexOf(UriTemplate.WildcardPath, StringComparison.Ordinal) == 1) &&
 74226                        !segment.EndsWith("/", StringComparison.Ordinal) &&
 74227                        (segment.Length > UriTemplate.WildcardPath.Length + 2));
 228
 229                default:
 230                    Fx.Assert("Bad part type identification !");
 0231                    return false;
 232            }
 233        }
 234
 235        public static NameValueCollection ParseQueryString(string query)
 236        {
 237            // We are adjusting the parsing of UrlUtility.ParseQueryString, which identify
 238            //  ?wsdl as a null key with wsdl as a value
 1239            NameValueCollection result = UrlUtility.ParseQueryString(query);
 1240            string nullKeyValuesString = result[null];
 1241            if (!string.IsNullOrEmpty(nullKeyValuesString))
 242            {
 0243                result.Remove(null);
 0244                string[] nullKeyValues = nullKeyValuesString.Split(',');
 0245                for (int i = 0; i < nullKeyValues.Length; i++)
 246                {
 0247                    result.Add(nullKeyValues[i], null);
 248                }
 249            }
 250
 1251            return result;
 252        }
 253
 254        private static bool AllTemplatesAreEquivalent(IList<UriTemplate> array, int a, int b)
 255        {
 0256            for (int i = a; i < b - 1; ++i)
 257            {
 0258                if (!array[i].IsEquivalentTo(array[i + 1]))
 259                {
 0260                    return false;
 261                }
 262            }
 263
 0264            return true;
 265        }
 266
 267        private static void EnsureQueriesAreDistinct(UriTemplate[] array, int a, int b, bool allowDuplicateEquivalentUri
 268        {
 0269            Dictionary<string, byte> queryVarNamesWithLiteralVals = new Dictionary<string, byte>(StringComparer.OrdinalI
 0270            for (int i = a; i < b; ++i)
 271            {
 0272                foreach (KeyValuePair<string, UriTemplateQueryValue> kvp in array[i]._queries)
 273                {
 0274                    if (kvp.Value.Nature == UriTemplatePartType.Literal)
 275                    {
 0276                        if (!queryVarNamesWithLiteralVals.ContainsKey(kvp.Key))
 277                        {
 0278                            queryVarNamesWithLiteralVals.Add(kvp.Key, 0);
 279                        }
 280                    }
 281                }
 282            }
 283
 284            // now we have set of possibilities:
 285            // further refine to only those for whom all templates have literals
 0286            Dictionary<string, byte> queryVarNamesAllLiterals = new Dictionary<string, byte>(queryVarNamesWithLiteralVal
 0287            for (int i = a; i < b; ++i)
 288            {
 0289                foreach (string s in queryVarNamesWithLiteralVals.Keys)
 290                {
 0291                    if (!array[i]._queries.ContainsKey(s) || (array[i]._queries[s].Nature != UriTemplatePartType.Literal
 292                    {
 0293                        queryVarNamesAllLiterals.Remove(s);
 294                    }
 295                }
 296            }
 297
 0298            queryVarNamesWithLiteralVals = null; // ensure we don't reference this variable any more
 299            // now we have the set of names that every operation has as a literal
 0300            if (queryVarNamesAllLiterals.Count == 0)
 301            {
 0302                if (allowDuplicateEquivalentUriTemplates && AllTemplatesAreEquivalent(array, a, b))
 303                {
 304                    // we're ok, do nothing
 305                }
 306                else
 307                {
 0308                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(
 0309                        SR.UTTOtherAmbiguousQueries, array[a].ToString())));
 310                }
 311            }
 312
 313            // now just ensure that each template has a unique tuple of values for the names
 0314            string[][] upsLits = new string[b - a][];
 0315            for (int i = 0; i < b - a; ++i)
 316            {
 0317                upsLits[i] = GetQueryLiterals(array[i + a], queryVarNamesAllLiterals);
 318            }
 319
 0320            for (int i = 0; i < b - a; ++i)
 321            {
 0322                for (int j = i + 1; j < b - a; ++j)
 323                {
 0324                    if (Same(upsLits[i], upsLits[j]))
 325                    {
 0326                        if (!array[i + a].IsEquivalentTo(array[j + a]))
 327                        {
 0328                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.F
 0329                                SR.UTTAmbiguousQueries, array[a + i].ToString(), array[j + a].ToString())));
 330                        }
 331
 332                        Fx.Assert(array[i + a].IsEquivalentTo(array[j + a]), "bad equiv logic");
 0333                        if (!allowDuplicateEquivalentUriTemplates)
 334                        {
 0335                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.F
 0336                                SR.UTTDuplicate, array[a + i].ToString(), array[j + a].ToString())));
 337                        }
 338                    }
 339                }
 340            }
 341            // we're good.  whew!
 0342        }
 343
 344        private static string[] GetQueryLiterals(UriTemplate up, Dictionary<string, byte> queryVarNames)
 345        {
 0346            string[] queryLitVals = new string[queryVarNames.Count];
 0347            int i = 0;
 0348            foreach (string queryVarName in queryVarNames.Keys)
 349            {
 350                Fx.Assert(up._queries.ContainsKey(queryVarName), "query doesn't have name");
 351
 0352                UriTemplateQueryValue utqv = up._queries[queryVarName];
 353                Fx.Assert(utqv.Nature == UriTemplatePartType.Literal, "query for name is not literal");
 0354                if (utqv == UriTemplateQueryValue.Empty)
 355                {
 0356                    queryLitVals[i] = null;
 357                }
 358                else
 359                {
 0360                    queryLitVals[i] = ((UriTemplateLiteralQueryValue)(utqv)).AsRawUnescapedString();
 361                }
 0362                ++i;
 363            }
 364
 0365            return queryLitVals;
 366        }
 367
 368        private static bool Same(string[] a, string[] b)
 369        {
 370            Fx.Assert(a.Length == b.Length, "arrays not same length");
 371
 0372            for (int i = 0; i < a.Length; ++i)
 373            {
 0374                if (a[i] != b[i])
 375                {
 0376                    return false;
 377                }
 378            }
 379
 0380            return true;
 381        }
 382
 383        internal class UriTemplateQueryComparer : IComparer<UriTemplate>
 384        {
 385            public int Compare(UriTemplate x, UriTemplate y)
 386            {
 387                // sort the empty queries to the front
 0388                return Comparer<int>.Default.Compare(x._queries.Count, y._queries.Count);
 389            }
 390        }
 391
 392        internal class UriTemplateQueryKeyComparer : IEqualityComparer<string>
 393        {
 394            public bool Equals(string x, string y)
 395            {
 0396                return (string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
 397            }
 398
 399            public int GetHashCode(string obj)
 400            {
 0401                if (obj == null)
 402                {
 0403                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(obj));
 404                }
 405
 0406                return obj.ToUpperInvariant().GetHashCode();
 407            }
 408        }
 409    }
 410}