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

Side by Side Diff: device/sensors/data_fetcher_shared_memory_base_unittest.cc

Issue 2885203004: Refactor content/renderer/device_sensors to use device/generic_sensor instead of device/sensors (Closed)
Patch Set: updated content/renderer/BUILD.gn Created 3 years, 7 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
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "device/sensors/data_fetcher_shared_memory_base.h"
6
7 #include "base/logging.h"
8 #include "base/macros.h"
9 #include "base/process/process_handle.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/threading/thread.h"
13 #include "device/sensors/public/cpp/device_motion_hardware_buffer.h"
14 #include "device/sensors/public/cpp/device_orientation_hardware_buffer.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace device {
18
19 namespace {
20
21 class FakeDataFetcher : public DataFetcherSharedMemoryBase {
22 public:
23 FakeDataFetcher()
24 : start_motion_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
25 base::WaitableEvent::InitialState::NOT_SIGNALED),
26 start_orientation_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
27 base::WaitableEvent::InitialState::NOT_SIGNALED),
28 start_orientation_absolute_(
29 base::WaitableEvent::ResetPolicy::AUTOMATIC,
30 base::WaitableEvent::InitialState::NOT_SIGNALED),
31 stop_motion_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
32 base::WaitableEvent::InitialState::NOT_SIGNALED),
33 stop_orientation_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
34 base::WaitableEvent::InitialState::NOT_SIGNALED),
35 stop_orientation_absolute_(
36 base::WaitableEvent::ResetPolicy::AUTOMATIC,
37 base::WaitableEvent::InitialState::NOT_SIGNALED),
38 updated_motion_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
39 base::WaitableEvent::InitialState::NOT_SIGNALED),
40 updated_orientation_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
41 base::WaitableEvent::InitialState::NOT_SIGNALED),
42 updated_orientation_absolute_(
43 base::WaitableEvent::ResetPolicy::AUTOMATIC,
44 base::WaitableEvent::InitialState::NOT_SIGNALED),
45 motion_buffer_(nullptr),
46 orientation_buffer_(nullptr),
47 orientation_absolute_buffer_(nullptr) {}
48 ~FakeDataFetcher() override { Shutdown(); }
49
50 bool Init(ConsumerType consumer_type, void* buffer) {
51 EXPECT_TRUE(buffer);
52
53 switch (consumer_type) {
54 case CONSUMER_TYPE_MOTION:
55 motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer);
56 break;
57 case CONSUMER_TYPE_ORIENTATION:
58 orientation_buffer_ =
59 static_cast<DeviceOrientationHardwareBuffer*>(buffer);
60 break;
61 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
62 orientation_absolute_buffer_ =
63 static_cast<DeviceOrientationHardwareBuffer*>(buffer);
64 break;
65 default:
66 return false;
67 }
68 return true;
69 }
70
71 void UpdateMotion() {
72 DeviceMotionHardwareBuffer* buffer = GetMotionBuffer();
73 ASSERT_TRUE(buffer);
74 buffer->seqlock.WriteBegin();
75 buffer->data.interval = kDeviceSensorIntervalMicroseconds / 1000.;
76 buffer->seqlock.WriteEnd();
77 updated_motion_.Signal();
78 }
79
80 void UpdateOrientation() {
81 DeviceOrientationHardwareBuffer* buffer = GetOrientationBuffer();
82 ASSERT_TRUE(buffer);
83 buffer->seqlock.WriteBegin();
84 buffer->data.alpha = 1;
85 buffer->seqlock.WriteEnd();
86 updated_orientation_.Signal();
87 }
88
89 void UpdateOrientationAbsolute() {
90 DeviceOrientationHardwareBuffer* buffer = GetOrientationAbsoluteBuffer();
91 ASSERT_TRUE(buffer);
92 buffer->seqlock.WriteBegin();
93 buffer->data.alpha = 1;
94 buffer->data.absolute = true;
95 buffer->seqlock.WriteEnd();
96 updated_orientation_absolute_.Signal();
97 }
98
99 DeviceMotionHardwareBuffer* GetMotionBuffer() const { return motion_buffer_; }
100
101 DeviceOrientationHardwareBuffer* GetOrientationBuffer() const {
102 return orientation_buffer_;
103 }
104
105 DeviceOrientationHardwareBuffer* GetOrientationAbsoluteBuffer() const {
106 return orientation_absolute_buffer_;
107 }
108
109 void WaitForStart(ConsumerType consumer_type) {
110 switch (consumer_type) {
111 case CONSUMER_TYPE_MOTION:
112 start_motion_.Wait();
113 break;
114 case CONSUMER_TYPE_ORIENTATION:
115 start_orientation_.Wait();
116 break;
117 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
118 start_orientation_absolute_.Wait();
119 break;
120 }
121 }
122
123 void WaitForStop(ConsumerType consumer_type) {
124 switch (consumer_type) {
125 case CONSUMER_TYPE_MOTION:
126 stop_motion_.Wait();
127 break;
128 case CONSUMER_TYPE_ORIENTATION:
129 stop_orientation_.Wait();
130 break;
131 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
132 stop_orientation_absolute_.Wait();
133 break;
134 }
135 }
136
137 void WaitForUpdate(ConsumerType consumer_type) {
138 switch (consumer_type) {
139 case CONSUMER_TYPE_MOTION:
140 updated_motion_.Wait();
141 break;
142 case CONSUMER_TYPE_ORIENTATION:
143 updated_orientation_.Wait();
144 break;
145 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
146 updated_orientation_absolute_.Wait();
147 break;
148 }
149 }
150
151 protected:
152 base::WaitableEvent start_motion_;
153 base::WaitableEvent start_orientation_;
154 base::WaitableEvent start_orientation_absolute_;
155 base::WaitableEvent stop_motion_;
156 base::WaitableEvent stop_orientation_;
157 base::WaitableEvent stop_orientation_absolute_;
158 base::WaitableEvent updated_motion_;
159 base::WaitableEvent updated_orientation_;
160 base::WaitableEvent updated_orientation_absolute_;
161
162 private:
163 DeviceMotionHardwareBuffer* motion_buffer_;
164 DeviceOrientationHardwareBuffer* orientation_buffer_;
165 DeviceOrientationHardwareBuffer* orientation_absolute_buffer_;
166
167 DISALLOW_COPY_AND_ASSIGN(FakeDataFetcher);
168 };
169
170 class FakeNonPollingDataFetcher : public FakeDataFetcher {
171 public:
172 FakeNonPollingDataFetcher() : update_(true) {}
173 ~FakeNonPollingDataFetcher() override {}
174
175 bool Start(ConsumerType consumer_type, void* buffer) override {
176 Init(consumer_type, buffer);
177 switch (consumer_type) {
178 case CONSUMER_TYPE_MOTION:
179 if (update_)
180 UpdateMotion();
181 start_motion_.Signal();
182 break;
183 case CONSUMER_TYPE_ORIENTATION:
184 if (update_)
185 UpdateOrientation();
186 start_orientation_.Signal();
187 break;
188 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
189 if (update_)
190 UpdateOrientationAbsolute();
191 start_orientation_absolute_.Signal();
192 break;
193 default:
194 return false;
195 }
196 return true;
197 }
198
199 bool Stop(ConsumerType consumer_type) override {
200 switch (consumer_type) {
201 case CONSUMER_TYPE_MOTION:
202 stop_motion_.Signal();
203 break;
204 case CONSUMER_TYPE_ORIENTATION:
205 stop_orientation_.Signal();
206 break;
207 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
208 stop_orientation_absolute_.Signal();
209 break;
210 default:
211 return false;
212 }
213 return true;
214 }
215
216 void Fetch(unsigned consumer_bitmask) override {
217 FAIL() << "fetch should not be called, "
218 << "because this is a non-polling fetcher";
219 }
220
221 FetcherType GetType() const override { return FakeDataFetcher::GetType(); }
222 void set_update(bool update) { update_ = update; }
223
224 private:
225 bool update_;
226
227 DISALLOW_COPY_AND_ASSIGN(FakeNonPollingDataFetcher);
228 };
229
230 class FakePollingDataFetcher : public FakeDataFetcher {
231 public:
232 FakePollingDataFetcher() {}
233 ~FakePollingDataFetcher() override {}
234
235 bool Start(ConsumerType consumer_type, void* buffer) override {
236 EXPECT_TRUE(
237 GetPollingMessageLoop()->task_runner()->BelongsToCurrentThread());
238
239 Init(consumer_type, buffer);
240 switch (consumer_type) {
241 case CONSUMER_TYPE_MOTION:
242 start_motion_.Signal();
243 break;
244 case CONSUMER_TYPE_ORIENTATION:
245 start_orientation_.Signal();
246 break;
247 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
248 start_orientation_absolute_.Signal();
249 break;
250 default:
251 return false;
252 }
253 return true;
254 }
255
256 bool Stop(ConsumerType consumer_type) override {
257 EXPECT_TRUE(
258 GetPollingMessageLoop()->task_runner()->BelongsToCurrentThread());
259
260 switch (consumer_type) {
261 case CONSUMER_TYPE_MOTION:
262 stop_motion_.Signal();
263 break;
264 case CONSUMER_TYPE_ORIENTATION:
265 stop_orientation_.Signal();
266 break;
267 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
268 stop_orientation_absolute_.Signal();
269 break;
270 default:
271 return false;
272 }
273 return true;
274 }
275
276 void Fetch(unsigned consumer_bitmask) override {
277 EXPECT_TRUE(
278 GetPollingMessageLoop()->task_runner()->BelongsToCurrentThread());
279 EXPECT_TRUE(consumer_bitmask & CONSUMER_TYPE_ORIENTATION ||
280 consumer_bitmask & CONSUMER_TYPE_ORIENTATION_ABSOLUTE ||
281 consumer_bitmask & CONSUMER_TYPE_MOTION);
282
283 if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION)
284 UpdateOrientation();
285 if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION_ABSOLUTE)
286 UpdateOrientationAbsolute();
287 if (consumer_bitmask & CONSUMER_TYPE_MOTION)
288 UpdateMotion();
289 }
290
291 FetcherType GetType() const override { return FETCHER_TYPE_POLLING_CALLBACK; }
292
293 private:
294 DISALLOW_COPY_AND_ASSIGN(FakePollingDataFetcher);
295 };
296
297 class FakeZeroDelayPollingDataFetcher : public FakeDataFetcher {
298 public:
299 FakeZeroDelayPollingDataFetcher() {}
300 ~FakeZeroDelayPollingDataFetcher() override {}
301
302 bool Start(ConsumerType consumer_type, void* buffer) override {
303 EXPECT_TRUE(
304 GetPollingMessageLoop()->task_runner()->BelongsToCurrentThread());
305
306 Init(consumer_type, buffer);
307 switch (consumer_type) {
308 case CONSUMER_TYPE_MOTION:
309 start_motion_.Signal();
310 break;
311 case CONSUMER_TYPE_ORIENTATION:
312 start_orientation_.Signal();
313 break;
314 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
315 start_orientation_absolute_.Signal();
316 break;
317 default:
318 return false;
319 }
320 return true;
321 }
322
323 bool Stop(ConsumerType consumer_type) override {
324 EXPECT_TRUE(
325 GetPollingMessageLoop()->task_runner()->BelongsToCurrentThread());
326
327 switch (consumer_type) {
328 case CONSUMER_TYPE_MOTION:
329 stop_motion_.Signal();
330 break;
331 case CONSUMER_TYPE_ORIENTATION:
332 stop_orientation_.Signal();
333 break;
334 case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
335 stop_orientation_absolute_.Signal();
336 break;
337 default:
338 return false;
339 }
340 return true;
341 }
342
343 void Fetch(unsigned consumer_bitmask) override {
344 FAIL() << "fetch should not be called";
345 }
346
347 FetcherType GetType() const override { return FETCHER_TYPE_SEPARATE_THREAD; }
348
349 bool IsPollingTimerRunningForTesting() const {
350 return FakeDataFetcher::IsPollingTimerRunningForTesting();
351 }
352
353 private:
354 DISALLOW_COPY_AND_ASSIGN(FakeZeroDelayPollingDataFetcher);
355 };
356
357 TEST(DataFetcherSharedMemoryBaseTest, DoesStartMotion) {
358 FakeNonPollingDataFetcher fake_data_fetcher;
359 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_DEFAULT,
360 fake_data_fetcher.GetType());
361
362 EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
363 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION);
364
365 EXPECT_EQ(kDeviceSensorIntervalMicroseconds / 1000.,
366 fake_data_fetcher.GetMotionBuffer()->data.interval);
367
368 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
369 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION);
370 }
371
372 TEST(DataFetcherSharedMemoryBaseTest, DoesStartOrientation) {
373 FakeNonPollingDataFetcher fake_data_fetcher;
374 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_DEFAULT,
375 fake_data_fetcher.GetType());
376
377 EXPECT_TRUE(
378 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
379 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
380
381 EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
382
383 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
384 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
385 }
386
387 TEST(DataFetcherSharedMemoryBaseTest, DoesStartOrientationAbsolute) {
388 FakeNonPollingDataFetcher fake_data_fetcher;
389 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_DEFAULT,
390 fake_data_fetcher.GetType());
391
392 EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(
393 CONSUMER_TYPE_ORIENTATION_ABSOLUTE));
394 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
395
396 EXPECT_EQ(1, fake_data_fetcher.GetOrientationAbsoluteBuffer()->data.alpha);
397 EXPECT_TRUE(fake_data_fetcher.GetOrientationAbsoluteBuffer()->data.absolute);
398
399 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
400 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
401 }
402
403 TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotion) {
404 FakePollingDataFetcher fake_data_fetcher;
405 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_POLLING_CALLBACK,
406 fake_data_fetcher.GetType());
407
408 EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
409 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION);
410 fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_MOTION);
411
412 EXPECT_EQ(kDeviceSensorIntervalMicroseconds / 1000.,
413 fake_data_fetcher.GetMotionBuffer()->data.interval);
414
415 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
416 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION);
417 }
418
419 TEST(DataFetcherSharedMemoryBaseTest, DoesPollOrientation) {
420 FakePollingDataFetcher fake_data_fetcher;
421 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_POLLING_CALLBACK,
422 fake_data_fetcher.GetType());
423
424 EXPECT_TRUE(
425 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
426 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
427 fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_ORIENTATION);
428
429 EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
430
431 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
432 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
433 }
434
435 TEST(DataFetcherSharedMemoryBaseTest, DoesPollOrientationAbsolute) {
436 FakePollingDataFetcher fake_data_fetcher;
437 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_POLLING_CALLBACK,
438 fake_data_fetcher.GetType());
439
440 EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(
441 CONSUMER_TYPE_ORIENTATION_ABSOLUTE));
442 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
443 fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
444
445 EXPECT_EQ(1, fake_data_fetcher.GetOrientationAbsoluteBuffer()->data.alpha);
446 EXPECT_TRUE(fake_data_fetcher.GetOrientationAbsoluteBuffer()->data.absolute);
447
448 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
449 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
450 }
451
452 TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotionAndOrientation) {
453 FakePollingDataFetcher fake_data_fetcher;
454 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_POLLING_CALLBACK,
455 fake_data_fetcher.GetType());
456
457 EXPECT_TRUE(
458 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
459 mojo::ScopedSharedBufferHandle handle_orientation =
460 fake_data_fetcher.GetSharedMemoryHandle(CONSUMER_TYPE_ORIENTATION);
461 EXPECT_TRUE(handle_orientation.is_valid());
462
463 EXPECT_TRUE(fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
464 mojo::ScopedSharedBufferHandle handle_motion =
465 fake_data_fetcher.GetSharedMemoryHandle(CONSUMER_TYPE_MOTION);
466 EXPECT_TRUE(handle_motion.is_valid());
467
468 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
469 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_MOTION);
470
471 fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_ORIENTATION);
472 fake_data_fetcher.WaitForUpdate(CONSUMER_TYPE_MOTION);
473
474 EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
475 EXPECT_EQ(kDeviceSensorIntervalMicroseconds / 1000.,
476 fake_data_fetcher.GetMotionBuffer()->data.interval);
477
478 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
479 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
480 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
481 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_MOTION);
482 }
483
484 TEST(DataFetcherSharedMemoryBaseTest, DoesNotPollZeroDelay) {
485 FakeZeroDelayPollingDataFetcher fake_data_fetcher;
486 EXPECT_EQ(DataFetcherSharedMemoryBase::FETCHER_TYPE_SEPARATE_THREAD,
487 fake_data_fetcher.GetType());
488
489 EXPECT_TRUE(
490 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
491 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
492
493 EXPECT_FALSE(fake_data_fetcher.IsPollingTimerRunningForTesting());
494 EXPECT_EQ(0, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
495
496 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
497 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
498 }
499
500 TEST(DataFetcherSharedMemoryBaseTest, DoesClearBufferOnStart) {
501 FakeNonPollingDataFetcher fake_data_fetcher;
502 EXPECT_TRUE(
503 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
504 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
505 EXPECT_EQ(1, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
506 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
507 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
508
509 // Restart orientation without updating the memory buffer and check that
510 // it has been cleared to its initial state.
511 fake_data_fetcher.set_update(false);
512 EXPECT_TRUE(
513 fake_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_ORIENTATION));
514 fake_data_fetcher.WaitForStart(CONSUMER_TYPE_ORIENTATION);
515 EXPECT_EQ(0, fake_data_fetcher.GetOrientationBuffer()->data.alpha);
516 fake_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
517 fake_data_fetcher.WaitForStop(CONSUMER_TYPE_ORIENTATION);
518 }
519
520 } // namespace
521
522 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698