File: StringResourceVerifier.cs

package info (click to toggle)
mono-reference-assemblies 3.12.1%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 604,240 kB
  • ctags: 625,505
  • sloc: cs: 3,967,741; xml: 2,793,081; ansic: 418,042; java: 60,435; sh: 14,833; makefile: 11,576; sql: 7,956; perl: 1,467; cpp: 1,446; yacc: 1,203; python: 598; asm: 422; sed: 16; php: 1
file content (227 lines) | stat: -rw-r--r-- 11,563 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace System.Data.Entity
{
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using Xunit;

    /// <summary>
    ///     Helpers for verifying strings that are pulled from a localized resources
    /// </summary>
    public class StringResourceVerifier
    {
        private readonly AssemblyResourceLookup _lookup;

        /// <summary>
        ///     Initializes a new instance of the StringResourceVerifier class.
        /// </summary>
        /// <param name="lookup"> Lookup to be used for locating string resources </param>
        public StringResourceVerifier(AssemblyResourceLookup lookup)
        {
            _lookup = lookup;
        }

        /// <summary>
        ///     Determines if the supplied string is an instance of the string defined in localized resources
        /// </summary>
        /// <param name="expectedResourceKey"> The key of the resource string to match against </param>
        /// <param name="actualMessage"> String value to be verified </param>
        /// <param name="stringParameters"> Expected values for string.Format placeholders in the resource string If none are supplied then any values for placeholders in the resource string will count as a match </param>
        /// <returns> True if the string matches, false otherwise </returns>
        public bool IsMatch(string expectedResourceKey, string actualMessage, params object[] stringParameters)
        {
            return IsMatch(expectedResourceKey, actualMessage, true, stringParameters);
        }

        /// <summary>
        ///     Determines if the supplied string is an instance of the string defined in localized resources
        /// </summary>
        /// <param name="expectedResourceKey"> The key of the resource string to match against </param>
        /// <param name="actualMessage"> String value to be verified </param>
        /// <param name="isExactMatch"> Determines whether the exception message must be exact match of the message in the resource file, or just contain it. </param>
        /// <param name="stringParameters"> Expected values for string.Format placeholders in the resource string If none are supplied then any values for placeholders in the resource string will count as a match </param>
        /// <returns> True if the string matches, false otherwise </returns>
        public bool IsMatch(string expectedResourceKey, string actualMessage, bool isExactMatch, params object[] stringParameters)
        {
            string messageFromResources;
            return IsMatch(expectedResourceKey, actualMessage, isExactMatch, stringParameters, out messageFromResources);
        }

        /// <summary>
        ///     Verified the supplied string is an instance of the string defined in resources
        /// </summary>
        /// <param name="expectedResourceKey"> The key of the resource string to match against </param>
        /// <param name="actualMessage"> String value to be verified </param>
        /// <param name="stringParameters"> Expected values for string.Format placeholders in the resource string If none are supplied then any values for placeholders in the resource string will count as a match </param>
        public void VerifyMatch(string expectedResourceKey, string actualMessage, params object[] stringParameters)
        {
            VerifyMatch(expectedResourceKey, actualMessage, true, stringParameters);
        }

        /// <summary>
        ///     Determines if the supplied string is an instance of the string defined in localized resources
        ///     If the string in the resource file contains string.Format place holders then the actual message can contain any values for these
        /// </summary>
        /// <param name="expectedResourceKey"> The key of the resource string to match against </param>
        /// <param name="actualMessage"> String value to be verified </param>
        /// <param name="isExactMatch"> Determines whether the exception message must be exact match of the message in the resource file, or just contain it. </param>
        /// <param name="stringParameters"> Expected values for string.Format placeholders in the resource string If none are supplied then any values for placeholders in the resource string will count as a match </param>
        public void VerifyMatch(string expectedResourceKey, string actualMessage, bool isExactMatch, params object[] stringParameters)
        {
            string messageFromResources;
            Assert.True(
                IsMatch(
                    expectedResourceKey, actualMessage, isExactMatch, stringParameters,
                    out messageFromResources),
                string.Format(
                    @"Actual string does not match the message defined in resources.
Expected:'{0}',
Actual:'{1}'",
                    messageFromResources, actualMessage));
        }

        private static bool IsMatchWithAnyPlaceholderValues(string expectedMessage, string actualMessage, bool isExactMatch)
        {
            // Find the sections of the Exception message seperated by {x} tags
            var sections = FindMessageSections(expectedMessage);

            // Check that each section is present in the actual message in correct order
            var indexToCheckFrom = 0;
            foreach (var section in sections)
            {
                // Check if it is present in the actual message
                var index = actualMessage.IndexOf(section, indexToCheckFrom, StringComparison.Ordinal);
                if (index < 0)
                {
                    return false;
                }

                if (section.Length == 0
                    && section == sections.Last())
                {
                    // If the last section is a zero-length string
                    // then this indicates that the placeholder is the
                    // last thing in the resource. Thus every section
                    // matched and the placeholder takes up the rest of string
                    // from the actual message.
                    return true;
                }
                else
                {
                    // continue checking from the end of this section
                    // (Ensures sections are in the correct order)
                    indexToCheckFrom = index + section.Length;
                }
            }

            // If we reach the end then everything is a match
            return isExactMatch ? indexToCheckFrom == actualMessage.Length : indexToCheckFrom <= actualMessage.Length;
        }

        private static IEnumerable<string> FindMessageSections(string messageFromResources)
        {
            // Find the start and end index of each section of the string
            var sections = new List<StringSection>();

            // Start with a section that spans the whole string
            // While there are still place holders shorten the previous section to end at the next { and start a new section from the following }
            sections.Add(
                new StringSection
                    {
                        StartIndex = 0,
                        EndIndex = messageFromResources.Length
                    });
            var indexToCheckFrom = 0;
            while (messageFromResources.IndexOf("{", indexToCheckFrom, StringComparison.Ordinal) >= 0)
            {
                // Find the indexes to split the new section around
                var previous = sections.Last();
                var previousEndIndex = messageFromResources.IndexOf("{", indexToCheckFrom, StringComparison.Ordinal);
                var nextStartIndex = messageFromResources.IndexOf("}", previousEndIndex, StringComparison.Ordinal) + 1;

                // If there are no remaining closing tags then we are done
                if (nextStartIndex == 0)
                {
                    break;
                }
                else
                {
                    // Contents of place holder must be integer
                    int temp;
                    var intContents =
                        int.TryParse(messageFromResources.Substring(previousEndIndex + 1, nextStartIndex - previousEndIndex - 2), out temp);

                    // Place holder must not be escaped (i.e. {{0}})
                    var escaped = messageFromResources[previousEndIndex] == '{'
                                  && nextStartIndex < messageFromResources.Length
                                  && messageFromResources[nextStartIndex] == '}';

                    if (!intContents || escaped)
                    {
                        indexToCheckFrom++;
                        continue;
                    }
                }

                // Shorten the previous section to end at the {
                previous.EndIndex = previousEndIndex;

                // Add the remaining string after the } into a new section,
                // even if the '}' is the last character in the string. This
                // helps verification ensure that there is actually a wildcard
                // at this point in the string instead of the string ending
                // without a wildcard.
                sections.Add(
                    new StringSection
                        {
                            StartIndex = nextStartIndex,
                            EndIndex = messageFromResources.Length
                        });
                indexToCheckFrom = nextStartIndex;
            }

            // Pull out the sections
            return
                sections.Select(
                    s => messageFromResources.Substring(s.StartIndex, s.EndIndex - s.StartIndex).Replace("{{", "{").Replace("}}", "}"));
        }

        private bool IsMatch(
            string expectedResourceKey, string actualMessage, bool isExactMatch, object[] stringParameters, out string messageFromResources)
        {
            ExceptionHelpers.CheckStringArgumentIsNotNullOrEmpty(expectedResourceKey, "expectedResourceKey");
            ExceptionHelpers.CheckArgumentNotNull(actualMessage, "actualMessage");

            messageFromResources = _lookup.LookupString(expectedResourceKey);

            if (stringParameters.Length == 0)
            {
                return IsMatchWithAnyPlaceholderValues(messageFromResources, actualMessage, isExactMatch);
            }
            else
            {
                messageFromResources = string.Format(CultureInfo.InvariantCulture, messageFromResources, stringParameters);

                return isExactMatch ? actualMessage == messageFromResources : actualMessage.Contains(messageFromResources);
            }
        }

        /// <summary>
        ///     Represents a section of a string
        /// </summary>
        private class StringSection
        {
            /// <summary>
            ///     Gets or sets the index the section starts at
            /// </summary>
            public int StartIndex { get; set; }

            /// <summary>
            ///     Gets or sets the index the section ends at
            /// </summary>
            public int EndIndex { get; set; }
        }
    }
}