Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(240)

Side by Side Diff: packages/analyzer/test/src/task/options_work_manager_test.dart

Issue 2990843002: Removed fixed dependencies (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library test.src.task.html_work_manager_test; 5 library analyzer.test.src.task.options_work_manager_test;
6 6
7 import 'package:analyzer/error/error.dart' show AnalysisError;
8 import 'package:analyzer/exception/exception.dart';
7 import 'package:analyzer/src/context/cache.dart'; 9 import 'package:analyzer/src/context/cache.dart';
10 import 'package:analyzer/src/error/codes.dart' show AnalysisOptionsErrorCode;
8 import 'package:analyzer/src/generated/engine.dart' 11 import 'package:analyzer/src/generated/engine.dart'
9 show 12 show
10 AnalysisEngine, 13 AnalysisEngine,
11 AnalysisErrorInfo, 14 AnalysisErrorInfo,
12 AnalysisErrorInfoImpl, 15 AnalysisErrorInfoImpl,
13 CacheState, 16 CacheState,
14 ChangeNoticeImpl, 17 ChangeNoticeImpl,
15 InternalAnalysisContext; 18 InternalAnalysisContext;
16 import 'package:analyzer/src/generated/error.dart'
17 show AnalysisError, HtmlErrorCode;
18 import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
19 import 'package:analyzer/src/generated/source.dart'; 19 import 'package:analyzer/src/generated/source.dart';
20 import 'package:analyzer/src/task/html.dart'; 20 import 'package:analyzer/src/task/options.dart';
21 import 'package:analyzer/src/task/html_work_manager.dart'; 21 import 'package:analyzer/src/task/options_work_manager.dart';
22 import 'package:analyzer/task/dart.dart'; 22 import 'package:analyzer/task/dart.dart';
23 import 'package:analyzer/task/general.dart'; 23 import 'package:analyzer/task/general.dart';
24 import 'package:analyzer/task/html.dart';
25 import 'package:analyzer/task/model.dart'; 24 import 'package:analyzer/task/model.dart';
25 import 'package:test_reflective_loader/test_reflective_loader.dart';
26 import 'package:typed_mock/typed_mock.dart'; 26 import 'package:typed_mock/typed_mock.dart';
27 import 'package:unittest/unittest.dart'; 27 import 'package:unittest/unittest.dart';
28 28
29 import '../../generated/test_support.dart'; 29 import '../../generated/test_support.dart';
30 import '../../reflective_tests.dart';
31 import '../../utils.dart'; 30 import '../../utils.dart';
32 31
33 main() { 32 main() {
34 initializeTestEnvironment(); 33 initializeTestEnvironment();
35 runReflectiveTests(HtmlWorkManagerTest); 34 defineReflectiveTests(OptionsWorkManagerNewFileTest);
35 defineReflectiveTests(OptionsWorkManagerOldFileTest);
36 } 36 }
37 37
38 @reflectiveTest 38 @reflectiveTest
39 class HtmlWorkManagerTest { 39 class OptionsWorkManagerNewFileTest extends OptionsWorkManagerTest {
40 String get optionsFile => AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE;
41 }
42
43 @reflectiveTest
44 class OptionsWorkManagerOldFileTest extends OptionsWorkManagerTest {
45 String get optionsFile => AnalysisEngine.ANALYSIS_OPTIONS_FILE;
46 }
47
48 abstract class OptionsWorkManagerTest {
40 InternalAnalysisContext context = new _InternalAnalysisContextMock(); 49 InternalAnalysisContext context = new _InternalAnalysisContextMock();
41 AnalysisCache cache; 50 AnalysisCache cache;
42 HtmlWorkManager manager; 51 OptionsWorkManager manager;
43 52
44 CaughtException caughtException = new CaughtException(null, null); 53 CaughtException caughtException = new CaughtException(null, null);
45 54
46 Source source1 = new TestSource('1.html'); 55 Source source1;
47 Source source2 = new TestSource('2.html'); 56
48 Source source3 = new TestSource('3.html'); 57 Source source2;
49 Source source4 = new TestSource('4.html'); 58 Source source3;
59 Source source4;
50 CacheEntry entry1; 60 CacheEntry entry1;
51 CacheEntry entry2; 61 CacheEntry entry2;
52 CacheEntry entry3; 62 CacheEntry entry3;
53 CacheEntry entry4; 63 CacheEntry entry4;
64 String get optionsFile;
54 65
55 void expect_sourceQueue(List<Source> sources) { 66 void expect_sourceQueue(List<Source> sources) {
56 expect(manager.sourceQueue, unorderedEquals(sources)); 67 expect(manager.sourceQueue, unorderedEquals(sources));
57 } 68 }
58 69
59 void setUp() { 70 void setUp() {
60 cache = context.analysisCache; 71 cache = context.analysisCache;
61 manager = new HtmlWorkManager(context); 72 manager = new OptionsWorkManager(context);
73 source1 = new TestSource('test1/$optionsFile');
74 source2 = new TestSource('test2/$optionsFile');
75 source3 = new TestSource('test3/$optionsFile');
76 source4 = new TestSource('test4/$optionsFile');
62 entry1 = context.getCacheEntry(source1); 77 entry1 = context.getCacheEntry(source1);
63 entry2 = context.getCacheEntry(source2); 78 entry2 = context.getCacheEntry(source2);
64 entry3 = context.getCacheEntry(source3); 79 entry3 = context.getCacheEntry(source3);
65 entry4 = context.getCacheEntry(source4); 80 entry4 = context.getCacheEntry(source4);
66 } 81 }
67 82
68 void test_applyChange_add() { 83 void test_applyChange_add() {
69 // add source1 84 // add source1
70 manager.applyChange([source1], [], []); 85 manager.applyChange([source1], [], []);
71 expect_sourceQueue([source1]); 86 expect_sourceQueue([source1]);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 expect_sourceQueue([source2]); 118 expect_sourceQueue([source2]);
104 // remove source2 119 // remove source2
105 manager.applyChange([], [], [source2]); 120 manager.applyChange([], [], [source2]);
106 expect_sourceQueue([]); 121 expect_sourceQueue([]);
107 // remove source3 122 // remove source3
108 manager.applyChange([], [], [source3]); 123 manager.applyChange([], [], [source3]);
109 expect_sourceQueue([]); 124 expect_sourceQueue([]);
110 } 125 }
111 126
112 void test_applyPriorityTargets() { 127 void test_applyPriorityTargets() {
113 when(context.shouldErrorsBeAnalyzed(source2, null)).thenReturn(true); 128 when(context.shouldErrorsBeAnalyzed(source2)).thenReturn(true);
114 when(context.shouldErrorsBeAnalyzed(source3, null)).thenReturn(true); 129 when(context.shouldErrorsBeAnalyzed(source3)).thenReturn(true);
115 manager.priorityResultQueue.add(new TargetedResult(source1, HTML_ERRORS)); 130 manager.priorityResultQueue
116 manager.priorityResultQueue.add(new TargetedResult(source2, HTML_ERRORS)); 131 .add(new TargetedResult(source1, ANALYSIS_OPTIONS_ERRORS));
132 manager.priorityResultQueue
133 .add(new TargetedResult(source2, ANALYSIS_OPTIONS_ERRORS));
117 // -source1 +source3 134 // -source1 +source3
118 manager.applyPriorityTargets([source2, source3]); 135 manager.applyPriorityTargets([source2, source3]);
119 expect( 136 expect(
120 manager.priorityResultQueue, 137 manager.priorityResultQueue,
121 unorderedEquals([ 138 unorderedEquals([
122 new TargetedResult(source2, HTML_ERRORS), 139 new TargetedResult(source2, ANALYSIS_OPTIONS_ERRORS),
123 new TargetedResult(source3, HTML_ERRORS) 140 new TargetedResult(source3, ANALYSIS_OPTIONS_ERRORS)
124 ])); 141 ]));
125 // get next request 142 // get next request
126 TargetedResult request = manager.getNextResult(); 143 TargetedResult request = manager.getNextResult();
127 expect(request.target, source2); 144 expect(request.target, source2);
128 expect(request.result, HTML_ERRORS); 145 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
129 } 146 }
130 147
131 void test_getErrors_fullList() { 148 void test_getErrors() {
132 AnalysisError error1 = 149 AnalysisError error1 = new AnalysisError(
133 new AnalysisError(source1, 1, 0, HtmlErrorCode.PARSE_ERROR, ['']); 150 source1, 1, 0, AnalysisOptionsErrorCode.PARSE_ERROR, ['']);
134 AnalysisError error2 = 151 AnalysisError error2 = new AnalysisError(
135 new AnalysisError(source1, 2, 0, HtmlErrorCode.PARSE_ERROR, ['']); 152 source1, 2, 0, AnalysisOptionsErrorCode.PARSE_ERROR, ['']);
136 entry1.setValue(HTML_DOCUMENT_ERRORS, <AnalysisError>[error1], []); 153 entry1
137 154 .setValue(ANALYSIS_OPTIONS_ERRORS, <AnalysisError>[error1, error2], []);
138 DartScript script = new DartScript(source1, []);
139 entry1.setValue(DART_SCRIPTS, [script], []);
140 CacheEntry scriptEntry = context.getCacheEntry(script);
141 scriptEntry.setValue(DART_ERRORS, [error2], []);
142
143 List<AnalysisError> errors = manager.getErrors(source1);
144 expect(errors, unorderedEquals([error1, error2]));
145 }
146
147 void test_getErrors_partialList() {
148 AnalysisError error1 =
149 new AnalysisError(source1, 1, 0, HtmlErrorCode.PARSE_ERROR, ['']);
150 AnalysisError error2 =
151 new AnalysisError(source1, 2, 0, HtmlErrorCode.PARSE_ERROR, ['']);
152 entry1.setValue(HTML_DOCUMENT_ERRORS, <AnalysisError>[error1, error2], []);
153 155
154 List<AnalysisError> errors = manager.getErrors(source1); 156 List<AnalysisError> errors = manager.getErrors(source1);
155 expect(errors, unorderedEquals([error1, error2])); 157 expect(errors, unorderedEquals([error1, error2]));
156 } 158 }
157 159
158 void test_getNextResult_hasNormal_firstIsError() { 160 void test_getNextResult_hasNormal_firstIsError() {
159 entry1.setErrorState(caughtException, [HTML_ERRORS]); 161 entry1.setErrorState(caughtException, [ANALYSIS_OPTIONS_ERRORS]);
160 manager.sourceQueue.addAll([source1, source2]); 162 manager.sourceQueue.addAll([source1, source2]);
161 TargetedResult request = manager.getNextResult(); 163 TargetedResult request = manager.getNextResult();
162 expect(request.target, source2); 164 expect(request.target, source2);
163 expect(request.result, HTML_ERRORS); 165 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
164 // source1 is out, source2 is waiting 166 // source1 is out, source2 is waiting
165 expect_sourceQueue([source2]); 167 expect_sourceQueue([source2]);
166 } 168 }
167 169
168 void test_getNextResult_hasNormal_firstIsInvalid() { 170 void test_getNextResult_hasNormal_firstIsInvalid() {
169 entry1.setState(HTML_ERRORS, CacheState.INVALID); 171 entry1.setState(ANALYSIS_OPTIONS_ERRORS, CacheState.INVALID);
170 manager.sourceQueue.addAll([source1, source2]); 172 manager.sourceQueue.addAll([source1, source2]);
171 TargetedResult request = manager.getNextResult(); 173 TargetedResult request = manager.getNextResult();
172 expect(request.target, source1); 174 expect(request.target, source1);
173 expect(request.result, HTML_ERRORS); 175 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
174 // no changes until computed 176 // no changes until computed
175 expect_sourceQueue([source1, source2]); 177 expect_sourceQueue([source1, source2]);
176 } 178 }
177 179
178 void test_getNextResult_hasNormal_firstIsValid() { 180 void test_getNextResult_hasNormal_firstIsValid() {
179 entry1.setValue(HTML_ERRORS, [], []); 181 entry1.setValue(ANALYSIS_OPTIONS_ERRORS, [], []);
180 manager.sourceQueue.addAll([source1, source2]); 182 manager.sourceQueue.addAll([source1, source2]);
181 TargetedResult request = manager.getNextResult(); 183 TargetedResult request = manager.getNextResult();
182 expect(request.target, source2); 184 expect(request.target, source2);
183 expect(request.result, HTML_ERRORS); 185 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
184 // source1 is out, source2 is waiting 186 // source1 is out, source2 is waiting
185 expect_sourceQueue([source2]); 187 expect_sourceQueue([source2]);
186 } 188 }
187 189
188 void test_getNextResult_hasNormalAndPriority() { 190 void test_getNextResult_hasNormalAndPriority() {
189 entry1.setState(HTML_ERRORS, CacheState.INVALID); 191 entry1.setState(ANALYSIS_OPTIONS_ERRORS, CacheState.INVALID);
190 manager.sourceQueue.addAll([source1, source2]); 192 manager.sourceQueue.addAll([source1, source2]);
191 manager.addPriorityResult(source3, HTML_ERRORS); 193 manager.addPriorityResult(source3, ANALYSIS_OPTIONS_ERRORS);
192 194
193 TargetedResult request = manager.getNextResult(); 195 TargetedResult request = manager.getNextResult();
194 expect(request.target, source3); 196 expect(request.target, source3);
195 expect(request.result, HTML_ERRORS); 197 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
196 // no changes until computed 198 // no changes until computed
197 expect_sourceQueue([source1, source2]); 199 expect_sourceQueue([source1, source2]);
198 } 200 }
199 201
200 void test_getNextResult_hasPriority() { 202 void test_getNextResult_hasPriority() {
201 manager.addPriorityResult(source1, HTML_ERRORS); 203 manager.addPriorityResult(source1, ANALYSIS_OPTIONS_ERRORS);
202 manager.addPriorityResult(source2, HTML_ERRORS); 204 manager.addPriorityResult(source2, ANALYSIS_OPTIONS_ERRORS);
203 expect( 205 expect(
204 manager.priorityResultQueue, 206 manager.priorityResultQueue,
205 unorderedEquals([ 207 unorderedEquals([
206 new TargetedResult(source1, HTML_ERRORS), 208 new TargetedResult(source1, ANALYSIS_OPTIONS_ERRORS),
207 new TargetedResult(source2, HTML_ERRORS) 209 new TargetedResult(source2, ANALYSIS_OPTIONS_ERRORS)
208 ])); 210 ]));
209 211
210 TargetedResult request = manager.getNextResult(); 212 TargetedResult request = manager.getNextResult();
211 expect(request.target, source1); 213 expect(request.target, source1);
212 expect(request.result, HTML_ERRORS); 214 expect(request.result, ANALYSIS_OPTIONS_ERRORS);
213 // no changes until computed 215 // no changes until computed
214 expect( 216 expect(
215 manager.priorityResultQueue, 217 manager.priorityResultQueue,
216 unorderedEquals([ 218 unorderedEquals([
217 new TargetedResult(source1, HTML_ERRORS), 219 new TargetedResult(source1, ANALYSIS_OPTIONS_ERRORS),
218 new TargetedResult(source2, HTML_ERRORS) 220 new TargetedResult(source2, ANALYSIS_OPTIONS_ERRORS)
219 ])); 221 ]));
220 } 222 }
221 223
222 void test_getNextResult_nothingToDo() { 224 void test_getNextResult_nothingToDo() {
223 TargetedResult request = manager.getNextResult(); 225 TargetedResult request = manager.getNextResult();
224 expect(request, isNull); 226 expect(request, isNull);
225 } 227 }
226 228
227 void test_getNextResultPriority_hasPriority() { 229 void test_getNextResultPriority_hasPriority() {
228 manager.addPriorityResult(source1, SOURCE_KIND); 230 manager.addPriorityResult(source1, SOURCE_KIND);
229 expect(manager.getNextResultPriority(), WorkOrderPriority.PRIORITY); 231 expect(manager.getNextResultPriority(), WorkOrderPriority.PRIORITY);
230 } 232 }
231 233
232 void test_getNextResultPriority_hasSource() { 234 void test_getNextResultPriority_hasSource() {
233 manager.sourceQueue.addAll([source1]); 235 manager.sourceQueue.addAll([source1]);
234 expect(manager.getNextResultPriority(), WorkOrderPriority.NORMAL); 236 expect(manager.getNextResultPriority(), WorkOrderPriority.NORMAL);
235 } 237 }
236 238
237 void test_getNextResultPriority_nothingToDo() { 239 void test_getNextResultPriority_nothingToDo() {
238 expect(manager.getNextResultPriority(), WorkOrderPriority.NONE); 240 expect(manager.getNextResultPriority(), WorkOrderPriority.NONE);
239 } 241 }
240 242
241 void test_onAnalysisOptionsChanged() {
242 when(context.exists(anyObject)).thenReturn(true);
243 // set cache values
244 entry1.setValue(DART_SCRIPTS, [], []);
245 entry1.setValue(HTML_DOCUMENT, null, []);
246 entry1.setValue(HTML_DOCUMENT_ERRORS, [], []);
247 entry1.setValue(HTML_ERRORS, [], []);
248 entry1.setValue(REFERENCED_LIBRARIES, [], []);
249 // notify
250 manager.onAnalysisOptionsChanged();
251 // Only resolution-based values are invalidated.
252 expect(entry1.getState(DART_SCRIPTS), CacheState.VALID);
253 expect(entry1.getState(HTML_DOCUMENT), CacheState.VALID);
254 expect(entry1.getState(HTML_DOCUMENT_ERRORS), CacheState.VALID);
255 expect(entry1.getState(HTML_ERRORS), CacheState.INVALID);
256 expect(entry1.getState(REFERENCED_LIBRARIES), CacheState.VALID);
257 }
258
259 void test_onResultInvalidated_scheduleInvalidatedLibraries() {
260 // set HTML_ERRORS for source1 and source3
261 entry1.setValue(HTML_ERRORS, [], []);
262 entry3.setValue(HTML_ERRORS, [], []);
263 // invalidate HTML_ERRORS for source1, schedule it
264 entry1.setState(HTML_ERRORS, CacheState.INVALID);
265 expect_sourceQueue([source1]);
266 // invalidate HTML_ERRORS for source3, schedule it
267 entry3.setState(HTML_ERRORS, CacheState.INVALID);
268 expect_sourceQueue([source1, source3]);
269 }
270
271 void test_onSourceFactoryChanged() {
272 when(context.exists(anyObject)).thenReturn(true);
273 // set cache values
274 entry1.setValue(DART_SCRIPTS, [], []);
275 entry1.setValue(HTML_DOCUMENT, null, []);
276 entry1.setValue(HTML_DOCUMENT_ERRORS, [], []);
277 entry1.setValue(HTML_ERRORS, [], []);
278 entry1.setValue(REFERENCED_LIBRARIES, [], []);
279 // notify
280 manager.onSourceFactoryChanged();
281 // Only resolution-based values are invalidated.
282 expect(entry1.getState(DART_SCRIPTS), CacheState.VALID);
283 expect(entry1.getState(HTML_DOCUMENT), CacheState.VALID);
284 expect(entry1.getState(HTML_DOCUMENT_ERRORS), CacheState.VALID);
285 expect(entry1.getState(HTML_ERRORS), CacheState.INVALID);
286 expect(entry1.getState(REFERENCED_LIBRARIES), CacheState.INVALID);
287 }
288
289 void test_resultsComputed_errors() { 243 void test_resultsComputed_errors() {
290 AnalysisError error1 = 244 AnalysisError error1 = new AnalysisError(
291 new AnalysisError(source1, 1, 0, HtmlErrorCode.PARSE_ERROR, ['']); 245 source1, 1, 0, AnalysisOptionsErrorCode.PARSE_ERROR, ['']);
292 AnalysisError error2 = 246 AnalysisError error2 = new AnalysisError(
293 new AnalysisError(source1, 2, 0, HtmlErrorCode.PARSE_ERROR, ['']); 247 source1, 2, 0, AnalysisOptionsErrorCode.PARSE_ERROR, ['']);
294 LineInfo lineInfo = new LineInfo([0]); 248 LineInfo lineInfo = new LineInfo([0]);
295 entry1.setValue(LINE_INFO, lineInfo, []); 249 entry1.setValue(LINE_INFO, lineInfo, []);
296 entry1.setValue(HTML_ERRORS, <AnalysisError>[error1, error2], []); 250 entry1
251 .setValue(ANALYSIS_OPTIONS_ERRORS, <AnalysisError>[error1, error2], []);
297 // RESOLVED_UNIT is ready, set errors 252 // RESOLVED_UNIT is ready, set errors
298 manager.resultsComputed(source1, {HTML_ERRORS: null}); 253 manager.resultsComputed(source1, {ANALYSIS_OPTIONS_ERRORS: null});
299 // all of the errors are included 254 // all of the errors are included
300 ChangeNoticeImpl notice = context.getNotice(source1); 255 ChangeNoticeImpl notice = context.getNotice(source1);
301 expect(notice.errors, unorderedEquals([error1, error2])); 256 expect(notice.errors, unorderedEquals([error1, error2]));
302 expect(notice.lineInfo, lineInfo); 257 expect(notice.lineInfo, lineInfo);
303 } 258 }
304 } 259 }
305 260
306 class _InternalAnalysisContextMock extends TypedMock 261 class _InternalAnalysisContextMock extends TypedMock
307 implements InternalAnalysisContext { 262 implements InternalAnalysisContext {
308 @override 263 @override
(...skipping 14 matching lines...) Expand all
323 CacheEntry entry = analysisCache.get(target); 278 CacheEntry entry = analysisCache.get(target);
324 if (entry == null) { 279 if (entry == null) {
325 entry = new CacheEntry(target); 280 entry = new CacheEntry(target);
326 analysisCache.put(entry); 281 analysisCache.put(entry);
327 } 282 }
328 return entry; 283 return entry;
329 } 284 }
330 285
331 @override 286 @override
332 AnalysisErrorInfo getErrors(Source source) { 287 AnalysisErrorInfo getErrors(Source source) {
333 String name = source.shortName;
334 List<AnalysisError> errors = AnalysisError.NO_ERRORS; 288 List<AnalysisError> errors = AnalysisError.NO_ERRORS;
335 if (AnalysisEngine.isDartFileName(name) || source is DartScript) { 289 if (AnalysisEngine.isAnalysisOptionsFileName(source.shortName)) {
336 errors = getCacheEntry(source).getValue(DART_ERRORS); 290 errors = getCacheEntry(source).getValue(ANALYSIS_OPTIONS_ERRORS);
337 } else if (AnalysisEngine.isHtmlFileName(name)) {
338 errors = getCacheEntry(source).getValue(HTML_ERRORS);
339 } 291 }
340 return new AnalysisErrorInfoImpl( 292 return new AnalysisErrorInfoImpl(
341 errors, getCacheEntry(source).getValue(LINE_INFO)); 293 errors, getCacheEntry(source).getValue(LINE_INFO));
342 } 294 }
343 295
344 @override 296 @override
345 ChangeNoticeImpl getNotice(Source source) { 297 ChangeNoticeImpl getNotice(Source source) =>
346 return _pendingNotices.putIfAbsent( 298 _pendingNotices.putIfAbsent(source, () => new ChangeNoticeImpl(source));
347 source, () => new ChangeNoticeImpl(source));
348 }
349
350 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
351 } 299 }
OLDNEW
« no previous file with comments | « packages/analyzer/test/src/task/options_test.dart ('k') | packages/analyzer/test/src/task/strong/checker_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698