|
2 | 2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
5 | | -/// Set of features used in annotations. |
| 5 | +import 'id_testing.dart'; |
| 6 | + |
| 7 | +/// Utility class for annotated testing representing a set of features. |
6 | 8 | class Features { |
7 | 9 | Map<String, Object> _features = {}; |
8 | 10 |
|
| 11 | + /// Mark the feature [key] as existing. If [value] is provided, the feature |
| 12 | + /// [key] is set to have this value. |
9 | 13 | void add(String key, {var value: ''}) { |
10 | 14 | _features[key] = value.toString(); |
11 | 15 | } |
12 | 16 |
|
| 17 | + /// Add [value] as an element of the list values of feature [key]. |
13 | 18 | void addElement(String key, [var value]) { |
14 | 19 | List<String> list = _features.putIfAbsent(key, () => <String>[]); |
15 | 20 | if (value != null) { |
16 | 21 | list.add(value.toString()); |
17 | 22 | } |
18 | 23 | } |
19 | 24 |
|
| 25 | + /// Returns `true` if feature [key] exists. |
20 | 26 | bool containsKey(String key) { |
21 | 27 | return _features.containsKey(key); |
22 | 28 | } |
23 | 29 |
|
| 30 | + /// Set the feature [key] to exist with the [value]. |
24 | 31 | void operator []=(String key, String value) { |
25 | 32 | _features[key] = value; |
26 | 33 | } |
27 | 34 |
|
| 35 | + /// Returns the value set for feature [key]. |
28 | 36 | Object operator [](String key) => _features[key]; |
29 | 37 |
|
| 38 | + /// Removes the value set for feature [key]. Returns the existing value. |
30 | 39 | Object remove(String key) => _features.remove(key); |
31 | 40 |
|
| 41 | + /// Returns `true` if this feature set is empty. |
32 | 42 | bool get isEmpty => _features.isEmpty; |
33 | 43 |
|
| 44 | + /// Returns `true` if this feature set is non-empty. |
34 | 45 | bool get isNotEmpty => _features.isNotEmpty; |
35 | 46 |
|
| 47 | + /// Call [f] for each feature in this feature set with its corresponding |
| 48 | + /// value. |
36 | 49 | void forEach(void Function(String, Object) f) { |
37 | 50 | _features.forEach(f); |
38 | 51 | } |
@@ -156,3 +169,106 @@ class Features { |
156 | 169 | return features; |
157 | 170 | } |
158 | 171 | } |
| 172 | + |
| 173 | +class FeaturesDataInterpreter implements DataInterpreter<Features> { |
| 174 | + const FeaturesDataInterpreter(); |
| 175 | + |
| 176 | + @override |
| 177 | + String isAsExpected(Features actualFeatures, String expectedData) { |
| 178 | + if (expectedData == '*') { |
| 179 | + return null; |
| 180 | + } else if (expectedData == '') { |
| 181 | + return actualFeatures.isNotEmpty ? "Expected empty data." : null; |
| 182 | + } else { |
| 183 | + List<String> errorsFound = []; |
| 184 | + Features expectedFeatures = Features.fromText(expectedData); |
| 185 | + Set<String> validatedFeatures = new Set<String>(); |
| 186 | + expectedFeatures.forEach((String key, Object expectedValue) { |
| 187 | + bool expectMatch = true; |
| 188 | + if (key.startsWith('!')) { |
| 189 | + key = key.substring(1); |
| 190 | + expectMatch = false; |
| 191 | + } |
| 192 | + validatedFeatures.add(key); |
| 193 | + Object actualValue = actualFeatures[key]; |
| 194 | + if (!expectMatch) { |
| 195 | + if (actualFeatures.containsKey(key)) { |
| 196 | + errorsFound.add('Unexpected data found for $key=$actualValue'); |
| 197 | + } |
| 198 | + } else if (!actualFeatures.containsKey(key)) { |
| 199 | + errorsFound.add('No data found for $key'); |
| 200 | + } else if (expectedValue == '') { |
| 201 | + if (actualValue != '') { |
| 202 | + errorsFound.add('Non-empty data found for $key'); |
| 203 | + } |
| 204 | + } else if (expectedValue == '*') { |
| 205 | + return; |
| 206 | + } else if (expectedValue is List) { |
| 207 | + if (actualValue is List) { |
| 208 | + List actualList = actualValue.toList(); |
| 209 | + for (Object expectedObject in expectedValue) { |
| 210 | + String expectedText = '$expectedObject'; |
| 211 | + bool matchFound = false; |
| 212 | + if (expectedText.endsWith('*')) { |
| 213 | + // Wildcard matcher. |
| 214 | + String prefix = |
| 215 | + expectedText.substring(0, expectedText.indexOf('*')); |
| 216 | + List matches = []; |
| 217 | + for (Object actualObject in actualList) { |
| 218 | + if ('$actualObject'.startsWith(prefix)) { |
| 219 | + matches.add(actualObject); |
| 220 | + matchFound = true; |
| 221 | + } |
| 222 | + } |
| 223 | + for (Object match in matches) { |
| 224 | + actualList.remove(match); |
| 225 | + } |
| 226 | + } else { |
| 227 | + for (Object actualObject in actualList) { |
| 228 | + if (expectedText == '$actualObject') { |
| 229 | + actualList.remove(actualObject); |
| 230 | + matchFound = true; |
| 231 | + break; |
| 232 | + } |
| 233 | + } |
| 234 | + } |
| 235 | + if (!matchFound) { |
| 236 | + errorsFound.add("No match found for $key=[$expectedText]"); |
| 237 | + } |
| 238 | + } |
| 239 | + if (actualList.isNotEmpty) { |
| 240 | + errorsFound |
| 241 | + .add("Extra data found $key=[${actualList.join(',')}]"); |
| 242 | + } |
| 243 | + } else { |
| 244 | + errorsFound.add("List data expected for $key: " |
| 245 | + "expected '$expectedValue', found '${actualValue}'"); |
| 246 | + } |
| 247 | + } else if (expectedValue != actualValue) { |
| 248 | + errorsFound.add( |
| 249 | + "Mismatch for $key: expected '$expectedValue', found '${actualValue}'"); |
| 250 | + } |
| 251 | + }); |
| 252 | + actualFeatures.forEach((String key, Object value) { |
| 253 | + if (!validatedFeatures.contains(key)) { |
| 254 | + if (value == '') { |
| 255 | + errorsFound.add("Extra data found '$key'"); |
| 256 | + } else { |
| 257 | + errorsFound.add("Extra data found $key=$value"); |
| 258 | + } |
| 259 | + } |
| 260 | + }); |
| 261 | + return errorsFound.isNotEmpty ? errorsFound.join('\n ') : null; |
| 262 | + } |
| 263 | + } |
| 264 | + |
| 265 | + @override |
| 266 | + String getText(Features actualData) { |
| 267 | + return actualData.getText(); |
| 268 | + } |
| 269 | + |
| 270 | + @override |
| 271 | + bool isEmpty(Features actualData) { |
| 272 | + return actualData == null || actualData.isEmpty; |
| 273 | + } |
| 274 | +} |
0 commit comments