blob: a66aab9882bc59d2fd63647719a383fd6dcd1373 [file] [log] [blame]
[email protected]e3c1fc92012-11-15 00:56:461// Copyright (c) 2012 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 "base/bind.h"
6#include "base/message_loop.h"
7#include "chromeos/dbus/mock_cros_disks_client.h"
8#include "chromeos/dbus/mock_dbus_thread_manager.h"
9#include "chromeos/disks/disk_mount_manager.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using chromeos::disks::DiskMountManager;
14using chromeos::CrosDisksClient;
15using chromeos::DBusThreadManager;
16using chromeos::MockCrosDisksClient;
17using chromeos::MockDBusThreadManager;
18using testing::_;
19using testing::Field;
20using testing::InSequence;
21
22namespace {
23
24// Holds information needed to create a DiskMountManager::Disk instance.
25struct TestDiskInfo {
26 const char* source_path;
27 const char* mount_path;
28 const char* system_path;
29 const char* file_path;
30 const char* device_label;
31 const char* drive_label;
32 const char* vendor_id;
33 const char* vendor_name;
34 const char* product_id;
35 const char* product_name;
36 const char* fs_uuid;
37 const char* system_path_prefix;
38 chromeos::DeviceType device_type;
39 uint64 size_in_bytes;
40 bool is_parent;
41 bool is_read_only;
42 bool has_media;
43 bool on_boot_device;
44 bool is_hidden;
45};
46
47// Holds information to create a DiskMOuntManager::MountPointInfo instance.
48struct TestMountPointInfo {
49 const char* source_path;
50 const char* mount_path;
51 chromeos::MountType mount_type;
52 chromeos::disks::MountCondition mount_condition;
53};
54
55// List of disks held in DiskMountManager at the begining of the test.
56const TestDiskInfo kTestDisks[] = {
57 {
58 "/device/source_path",
59 "/device/mount_path",
60 "/device/prefix/system_path",
61 "/device/file_path",
62 "/device/device_label",
63 "/device/drive_label",
64 "/device/vendor_id",
65 "/device/vendor_name",
66 "/device/product_id",
67 "/device/product_name",
68 "/device/fs_uuid",
69 "/device/prefix",
70 chromeos::DEVICE_TYPE_USB,
71 1073741824, // size in bytes
72 false, // is parent
73 false, // is read only
74 true, // has media
75 false, // is on boot device
76 false // is hidden
77 },
78};
79
80// List ofmount points held in DiskMountManager at the begining of the test.
81const TestMountPointInfo kTestMountPoints[] = {
82 {
83 "/archive/source_path",
84 "/archive/mount_path",
85 chromeos::MOUNT_TYPE_ARCHIVE,
86 chromeos::disks::MOUNT_CONDITION_NONE
87 },
88 {
89 "/device/source_path",
90 "/device/mount_path",
91 chromeos::MOUNT_TYPE_DEVICE,
92 chromeos::disks::MOUNT_CONDITION_NONE
93 },
94};
95
96// Mocks response from CrosDisksClient::Unmount.
97ACTION_P(MockUnmountPath, success) {
98 CrosDisksClient::UnmountCallback callback = success ? arg2 : arg3;
99 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
100 base::Bind(callback, arg0));
101}
102
103// Mocks response from CrosDisksClient::FormatDevice.
104ACTION_P(MockFormatDevice, success) {
105 if (success) {
106 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
107 base::Bind(arg2, arg0, true));
108 } else {
109 base::MessageLoopProxy::current()->PostTask(FROM_HERE, base::Bind(arg3));
110 }
111}
112
113// Mocks DiskMountManager observer.
114class MockDiskMountManagerObserver : public DiskMountManager::Observer {
115 public:
116 virtual ~MockDiskMountManagerObserver() {}
117
118 MOCK_METHOD2(OnDiskEvent, void(DiskMountManager::DiskEvent event,
119 const DiskMountManager::Disk* disk));
120 MOCK_METHOD2(OnDeviceEvent, void(DiskMountManager::DeviceEvent event,
121 const std::string& device_path));
122 MOCK_METHOD3(OnMountEvent,
123 void(DiskMountManager::MountEvent event,
124 chromeos::MountError error_code,
125 const DiskMountManager::MountPointInfo& mount_point));
126 MOCK_METHOD3(OnFormatEvent,
127 void(DiskMountManager::FormatEvent event,
128 chromeos::FormatError error_code,
129 const std::string& device_path));
130};
131
132class DiskMountManagerTest : public testing::Test {
133 public:
134 DiskMountManagerTest() {}
135 virtual ~DiskMountManagerTest() {}
136
137 // Sets up test dbus tread manager and disks mount manager.
138 // Initializes disk mount manager disks and mount points.
139 // Adds a test observer to the disk mount manager.
140 virtual void SetUp() {
141 MockDBusThreadManager* mock_thread_manager = new MockDBusThreadManager();
142 DBusThreadManager::InitializeForTesting(mock_thread_manager);
143
144 mock_cros_disks_client_ = mock_thread_manager->mock_cros_disks_client();
145
146 DiskMountManager::Initialize();
147
148 InitDisksAndMountPoints();
149
150 DiskMountManager::GetInstance()->AddObserver(&observer_);
151 }
152
153 // Shuts down dbus thread manager and disk moutn manager used in the test.
154 virtual void TearDown() {
155 DiskMountManager::GetInstance()->RemoveObserver(&observer_);
156 DiskMountManager::Shutdown();
157 DBusThreadManager::Shutdown();
158 }
159
160 protected:
161 // Checks if disk mount manager contains a mount point with specified moutn
162 // path.
163 bool HasMountPoint(const std::string& mount_path) {
164 const DiskMountManager::MountPointMap& mount_points =
165 DiskMountManager::GetInstance()->mount_points();
166 return mount_points.find(mount_path) != mount_points.end();
167 }
168
169 private:
170 // Adds a new disk to the disk mount manager.
171 void AddTestDisk(const TestDiskInfo& disk) {
172 EXPECT_TRUE(DiskMountManager::GetInstance()->AddDiskForTest(
173 new DiskMountManager::Disk(disk.source_path,
174 disk.mount_path,
175 disk.system_path,
176 disk.file_path,
177 disk.device_label,
178 disk.drive_label,
179 disk.vendor_id,
180 disk.vendor_name,
181 disk.product_id,
182 disk.product_name,
183 disk.fs_uuid,
184 disk.system_path_prefix,
185 disk.device_type,
186 disk.size_in_bytes,
187 disk.is_parent,
188 disk.is_read_only,
189 disk.has_media,
190 disk.on_boot_device,
191 disk.is_hidden)));
192 }
193
194 // Adds a new mount point to the disk mount manager.
195 // If the moutn point is a device mount point, disk with its source path
196 // should already be added to the disk mount manager.
197 void AddTestMountPoint(const TestMountPointInfo& mount_point) {
198 EXPECT_TRUE(DiskMountManager::GetInstance()->AddMountPointForTest(
199 DiskMountManager::MountPointInfo(mount_point.source_path,
200 mount_point.mount_path,
201 mount_point.mount_type,
202 mount_point.mount_condition)));
203 }
204
205 // Adds disks and mount points to disk mount manager.
206 void InitDisksAndMountPoints() {
207 // Disks should be added first (when adding device mount points it is
208 // expected that the corresponding disk is already added).
209 for (size_t i = 0; i < arraysize(kTestDisks); i++)
210 AddTestDisk(kTestDisks[i]);
211
212 for (size_t i = 0; i < arraysize(kTestMountPoints); i++)
213 AddTestMountPoint(kTestMountPoints[i]);
214 }
215
216 protected:
217 MockCrosDisksClient* mock_cros_disks_client_;
218 MockDiskMountManagerObserver observer_;
219 MessageLoopForUI message_loop_;
220};
221
222// Tests that the observer gets notified on attempt to format non existent mount
223// point.
224TEST_F(DiskMountManagerTest, Format_NotMounted) {
225 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
226 chromeos::FORMAT_ERROR_UNKNOWN,
227 "/mount/non_existent"))
228 .Times(1);
229 DiskMountManager::GetInstance()->FormatMountedDevice("/mount/non_existent");
230}
231
232// Tests that it is not possible to format archive mount point.
233TEST_F(DiskMountManagerTest, Format_Archive) {
234 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
235 chromeos::FORMAT_ERROR_UNKNOWN,
236 "/archive/source_path"))
237 .Times(1);
238
239 DiskMountManager::GetInstance()->FormatMountedDevice("/archive/mount_path");
240}
241
242// Tests that format fails if the device cannot be unmounted.
243TEST_F(DiskMountManagerTest, Format_FailToUnmount) {
244 // Set up cros disks client mocks.
245 // Before formatting mounted device, the device should be unmounted.
246 // In this test unmount will fail, and there should be no attempt to
247 // format the device.
248 EXPECT_CALL(*mock_cros_disks_client_,
249 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
250 _, _))
251 .WillOnce(MockUnmountPath(false));
252
253 EXPECT_CALL(*mock_cros_disks_client_,
254 FormatDevice("/device/mount_path", _, _, _))
255 .Times(0);
256
257 // Set up expectations for observer mock.
258 // Observer should be notified that unmount attempt fails and format task
259 // failed to start.
260 {
261 InSequence s;
262
263 EXPECT_CALL(observer_,
264 OnMountEvent(DiskMountManager::UNMOUNTING,
265 chromeos::MOUNT_ERROR_INTERNAL,
266 Field(&DiskMountManager::MountPointInfo::mount_path,
267 "/device/mount_path")))
268 .Times(1);
269
270 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
271 chromeos::FORMAT_ERROR_UNKNOWN,
272 "/device/source_path"))
273 .Times(1);
274 }
275
276 // Start test.
277 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
278
279 // Cros disks will respond asynchronoulsy, so let's drain the message loop.
280 message_loop_.RunUntilIdle();
281
282 // The device mount should still be here.
283 EXPECT_TRUE(HasMountPoint("/device/mount_path"));
284}
285
286// Tests that observer is notified when cros disks fails to start format
287// process.
288TEST_F(DiskMountManagerTest, Format_FormatFailsToStart) {
289 // Set up cros disks client mocks.
290 // Before formatting mounted device, the device should be unmounted.
291 // In this test, unmount will succeed, but call to FormatDevice method will
292 // fail.
293 EXPECT_CALL(*mock_cros_disks_client_,
294 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
295 _, _))
296 .WillOnce(MockUnmountPath(true));
297
298 EXPECT_CALL(*mock_cros_disks_client_,
299 FormatDevice("/device/source_path", "vfat", _, _))
300 .WillOnce(MockFormatDevice(false));
301
302 // Set up expectations for observer mock.
303 // Observer should be notified that the device was unmounted and format task
304 // failed to start.
305 {
306 InSequence s;
307
308 EXPECT_CALL(observer_,
309 OnMountEvent(DiskMountManager::UNMOUNTING,
310 chromeos::MOUNT_ERROR_NONE,
311 Field(&DiskMountManager::MountPointInfo::mount_path,
312 "/device/mount_path")))
313 .Times(1);
314
315 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
316 chromeos::FORMAT_ERROR_UNKNOWN,
317 "/device/source_path"))
318 .Times(1);
319 }
320
321 // Start the test.
322 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
323
324 // Cros disks will respond asynchronoulsy, so let's drain the message loop.
325 message_loop_.RunUntilIdle();
326
327 // The device mount should be gone.
328 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
329}
330
331// Tests the case where there are two format requests for the same device.
332TEST_F(DiskMountManagerTest, Format_ConcurrentFormatCalls) {
333 // Set up cros disks client mocks.
334 // Only the first format request should be processed (for the other one there
335 // should be an error before device unmount is attempted).
336 // CrosDisksClient will report that the format process for the first request
337 // is successfully started.
338 EXPECT_CALL(*mock_cros_disks_client_,
339 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
340 _, _))
341 .WillOnce(MockUnmountPath(true));
342
343 EXPECT_CALL(*mock_cros_disks_client_,
344 FormatDevice("/device/source_path", "vfat", _, _))
345 .WillOnce(MockFormatDevice(true));
346
347 // Set up expectations for observer mock.
348 // The observer should get two FORMAT_STARTED events, one for each format
349 // request, but with different error codes (the formatting will be started
350 // only for the first request).
351 // There should alos be one UNMOUNTING event, since the device should get
352 // unmounted for the first request.
353 //
354 // Note that in this test the format completion signal will not be simulated,
355 // so the observer should not get FORMAT_COMPLETED signal.
356 {
357 InSequence s;
358
359 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
360 chromeos::FORMAT_ERROR_UNKNOWN,
361 "/device/source_path"))
362 .Times(1);
363
364 EXPECT_CALL(observer_,
365 OnMountEvent(DiskMountManager::UNMOUNTING,
366 chromeos::MOUNT_ERROR_NONE,
367 Field(&DiskMountManager::MountPointInfo::mount_path,
368 "/device/mount_path")))
369 .Times(1);
370
371 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
372 chromeos::FORMAT_ERROR_NONE,
373 "/device/source_path"))
374 .Times(1);
375 }
376
377 // Start the test.
378 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
379 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
380
381 // Cros disks will respond asynchronoulsy, so let's drain the message loop.
382 message_loop_.RunUntilIdle();
383
384 // The device mount should be gone.
385 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
386}
387
388// Tests the case when the format process actually starts and fails.
389TEST_F(DiskMountManagerTest, Format_FormatFails) {
390 // Set up cros disks client mocks.
391 // Both unmount and format device cals are successfull in this test.
392 EXPECT_CALL(*mock_cros_disks_client_,
393 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
394 _, _))
395 .WillOnce(MockUnmountPath(true));
396
397 EXPECT_CALL(*mock_cros_disks_client_,
398 FormatDevice("/device/source_path", "vfat", _, _))
399 .WillOnce(MockFormatDevice(true));
400
401 // Set up expectations for observer mock.
402 // The observer should get notified that the device was unmounted and that
403 // formatting has started.
404 // After the formatting starts, the test will simulate failing
405 // FORMATTING_FINISHED signal, so the observer should also be notified the
406 // formatting has failed (FORMAT_COMPLETED event).
407 {
408 InSequence s;
409
410 EXPECT_CALL(observer_,
411 OnMountEvent(DiskMountManager::UNMOUNTING,
412 chromeos::MOUNT_ERROR_NONE,
413 Field(&DiskMountManager::MountPointInfo::mount_path,
414 "/device/mount_path")))
415 .Times(1);
416
417 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
418 chromeos::FORMAT_ERROR_NONE,
419 "/device/source_path"))
420 .Times(1);
421
422 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
423 chromeos::FORMAT_ERROR_UNKNOWN,
424 "/device/source_path"))
425 .Times(1);
426 }
427
428 // Start the test.
429 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
430
431 // Wait for Unmount and FormatDevice calls to end.
432 message_loop_.RunUntilIdle();
433
434 // The device should be unmounted by now.
435 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
436
437 // Send failing FORMATTING_FINISHED signal.
438 // The failure is marked by ! in fromt of the path (but this should change
439 // soon).
440 mock_cros_disks_client_->SendMountEvent(
441 chromeos::CROS_DISKS_FORMATTING_FINISHED, "!/device/source_path");
442}
443
444// Tests the same case as Format_FormatFails, but the FORMATTING_FINISHED event
445// is sent with file_path of the formatted device (instead of its device path).
446TEST_F(DiskMountManagerTest, Format_FormatFailsAndReturnFilePath) {
447 // Set up cros disks client mocks.
448 EXPECT_CALL(*mock_cros_disks_client_,
449 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
450 _, _))
451 .WillOnce(MockUnmountPath(true));
452
453 EXPECT_CALL(*mock_cros_disks_client_,
454 FormatDevice("/device/source_path", "vfat", _, _))
455 .WillOnce(MockFormatDevice(true));
456
457 // Set up expectations for observer mock.
458 {
459 InSequence s;
460
461 EXPECT_CALL(observer_,
462 OnMountEvent(DiskMountManager::UNMOUNTING,
463 chromeos::MOUNT_ERROR_NONE,
464 Field(&DiskMountManager::MountPointInfo::mount_path,
465 "/device/mount_path")))
466 .Times(1);
467
468 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
469 chromeos::FORMAT_ERROR_NONE,
470 "/device/source_path"))
471 .Times(1);
472
473 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
474 chromeos::FORMAT_ERROR_UNKNOWN,
475 "/device/source_path"))
476 .Times(1);
477 }
478
479 // Start test.
480 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
481
482 // Wait for Unmount and FormatDevice calls to end.
483 message_loop_.RunUntilIdle();
484
485 // The device should be unmounted by now.
486 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
487
488 // Send failing FORMATTING_FINISHED signal with the device's file path.
489 mock_cros_disks_client_->SendMountEvent(
490 chromeos::CROS_DISKS_FORMATTING_FINISHED, "!/device/file_path");
491}
492
493// Tests the case when formatting completes successfully.
494TEST_F(DiskMountManagerTest, Format_FormatSuccess) {
495 // Set up cros disks client mocks.
496 // Both unmount and format device cals are successfull in this test.
497 EXPECT_CALL(*mock_cros_disks_client_,
498 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
499 _, _))
500 .WillOnce(MockUnmountPath(true));
501
502 EXPECT_CALL(*mock_cros_disks_client_,
503 FormatDevice("/device/source_path", "vfat", _, _))
504 .WillOnce(MockFormatDevice(true));
505
506 // Set up expectations for observer mock.
507 // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
508 // events (all of them without an error set).
509 {
510 InSequence s;
511
512 EXPECT_CALL(observer_,
513 OnMountEvent(DiskMountManager::UNMOUNTING,
514 chromeos::MOUNT_ERROR_NONE,
515 Field(&DiskMountManager::MountPointInfo::mount_path,
516 "/device/mount_path")))
517 .Times(1);
518
519 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
520 chromeos::FORMAT_ERROR_NONE,
521 "/device/source_path"))
522 .Times(1);
523
524 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
525 chromeos::FORMAT_ERROR_NONE,
526 "/device/source_path"))
527 .Times(1);
528 }
529
530 // Start the test.
531 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
532
533 // Wait for Unmount and FormatDevice calls to end.
534 message_loop_.RunUntilIdle();
535
536 // The device should be unmounted by now.
537 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
538
539 // Simulate cros_disks reporting success.
540 mock_cros_disks_client_->SendMountEvent(
541 chromeos::CROS_DISKS_FORMATTING_FINISHED, "/device/source_path");
542}
543
544// Tests that it's possible to format the device twice in a row (this may not be
545// true if the list of pending formats is not properly cleared).
546TEST_F(DiskMountManagerTest, Format_ConsecutiveFormatCalls) {
547 // Set up cros disks client mocks.
548 // All unmount and format device cals are successfull in this test.
549 // Each of the should be made twice (once for each formatting task).
550 EXPECT_CALL(*mock_cros_disks_client_,
551 Unmount("/device/mount_path", chromeos::UNMOUNT_OPTIONS_NONE,
552 _, _))
553 .WillOnce(MockUnmountPath(true))
554 .WillOnce(MockUnmountPath(true));
555
556 EXPECT_CALL(*mock_cros_disks_client_,
557 FormatDevice("/device/source_path", "vfat", _, _))
558 .WillOnce(MockFormatDevice(true))
559 .WillOnce(MockFormatDevice(true));
560
561 // Set up expectations for observer mock.
562 // The observer should receive UNMOUNTING, FORMAT_STARTED and FORMAT_COMPLETED
563 // events (all of them without an error set) twice (once for each formatting
564 // task).
565 // Also, there should be a MOUNTING event when the device remounting is
566 // simulated.
567 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_COMPLETED,
568 chromeos::FORMAT_ERROR_NONE,
569 "/device/source_path"))
570 .Times(2);
571
572 EXPECT_CALL(observer_, OnFormatEvent(DiskMountManager::FORMAT_STARTED,
573 chromeos::FORMAT_ERROR_NONE,
574 "/device/source_path"))
575 .Times(2);
576
577 EXPECT_CALL(observer_,
578 OnMountEvent(DiskMountManager::UNMOUNTING,
579 chromeos::MOUNT_ERROR_NONE,
580 Field(&DiskMountManager::MountPointInfo::mount_path,
581 "/device/mount_path")))
582 .Times(2);
583
584 EXPECT_CALL(observer_,
585 OnMountEvent(DiskMountManager::MOUNTING,
586 chromeos::MOUNT_ERROR_NONE,
587 Field(&DiskMountManager::MountPointInfo::mount_path,
588 "/device/mount_path")))
589 .Times(1);
590
591 // Start the test.
592 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
593
594 // Wait for Unmount and FormatDevice calls to end.
595 message_loop_.RunUntilIdle();
596
597 // The device should be unmounted by now.
598 EXPECT_FALSE(HasMountPoint("/device/mount_path"));
599
600 // Simulate cros_disks reporting success.
601 mock_cros_disks_client_->SendMountEvent(
602 chromeos::CROS_DISKS_FORMATTING_FINISHED, "/device/source_path");
603
604 // Simulate the device remounting.
605 mock_cros_disks_client_->SendMountCompletedEvent(
606 chromeos::MOUNT_ERROR_NONE,
607 "/device/source_path",
608 chromeos::MOUNT_TYPE_DEVICE,
609 "/device/mount_path");
610
611 EXPECT_TRUE(HasMountPoint("/device/mount_path"));
612
613 // Try formatting again.
614 DiskMountManager::GetInstance()->FormatMountedDevice("/device/mount_path");
615
616 // Wait for Unmount and FormatDevice calls to end.
617 message_loop_.RunUntilIdle();
618
619 // Simulate cros_disks reporting success.
620 mock_cros_disks_client_->SendMountEvent(
621 chromeos::CROS_DISKS_FORMATTING_FINISHED, "/device/source_path");
622}
623
624} // namespace
625