You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

513 lines
18 KiB

пре 9 година
  1. -module(rebar_file_utils_SUITE).
  2. -export([all/0,
  3. groups/0,
  4. init_per_group/2,
  5. end_per_group/2,
  6. init_per_testcase/2,
  7. end_per_testcase/2,
  8. raw_tmpdir/1,
  9. empty_tmpdir/1,
  10. simple_tmpdir/1,
  11. multi_tmpdir/1,
  12. reset_nonexistent_dir/1,
  13. reset_empty_dir/1,
  14. reset_dir/1,
  15. path_from_ancestor/1,
  16. canonical_path/1,
  17. resolve_link/1,
  18. split_dirname/1,
  19. mv_warning_is_ignored/1,
  20. mv_dir/1,
  21. mv_file_same/1,
  22. mv_file_diff/1,
  23. mv_file_dir_same/1,
  24. mv_file_dir_diff/1,
  25. mv_no_clobber/1,
  26. symlink_existing_dir/1,
  27. preserve_existing_symlink/1,
  28. create_missing_dir/1,
  29. preserve_existing_dir/1,
  30. delete_symlink_and_create_dir/1,
  31. delete_dir_and_create_symlink/1]).
  32. -include_lib("common_test/include/ct.hrl").
  33. -include_lib("eunit/include/eunit.hrl").
  34. -include_lib("kernel/include/file.hrl").
  35. all() ->
  36. [{group, tmpdir},
  37. {group, reset_dir},
  38. {group, mv},
  39. path_from_ancestor,
  40. canonical_path,
  41. resolve_link,
  42. split_dirname,
  43. mv_warning_is_ignored,
  44. symlink_existing_dir,
  45. preserve_existing_symlink,
  46. create_missing_dir,
  47. preserve_existing_dir,
  48. delete_symlink_and_create_dir,
  49. delete_dir_and_create_symlink].
  50. groups() ->
  51. [{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]},
  52. {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]},
  53. {mv, [], [mv_dir, mv_file_same, mv_file_diff,
  54. mv_file_dir_same, mv_file_dir_diff, mv_no_clobber]}].
  55. init_per_group(reset_dir, Config) ->
  56. TmpDir = rebar_file_utils:system_tmpdir(["rebar_file_utils_SUITE", "resetable"]),
  57. [{tmpdir, TmpDir}|Config];
  58. init_per_group(_, Config) -> Config.
  59. end_per_group(_, Config) -> Config.
  60. init_per_testcase(Test, Config) ->
  61. case os:type() of
  62. {win32, _} ->
  63. case lists:member(Test, [resolve_link, mv_warning_is_ignored]) of
  64. true -> {skip, "broken in windows"};
  65. false -> Config
  66. end;
  67. _ ->
  68. Config
  69. end.
  70. end_per_testcase(_Test, Config) ->
  71. Config.
  72. raw_tmpdir(_Config) ->
  73. case rebar_file_utils:system_tmpdir() of
  74. "/tmp" -> ok;
  75. "./tmp" -> ok
  76. end.
  77. empty_tmpdir(_Config) ->
  78. case rebar_file_utils:system_tmpdir([]) of
  79. "/tmp" -> ok;
  80. "./tmp" -> ok
  81. end.
  82. simple_tmpdir(_Config) ->
  83. case rebar_file_utils:system_tmpdir(["test"]) of
  84. "/tmp/test" -> ok;
  85. "./tmp/test" -> ok
  86. end.
  87. multi_tmpdir(_Config) ->
  88. case rebar_file_utils:system_tmpdir(["a", "b", "c"]) of
  89. "/tmp/a/b/c" -> ok;
  90. "./tmp/a/b/c" -> ok
  91. end.
  92. reset_nonexistent_dir(Config) ->
  93. TmpDir = ?config(tmpdir, Config),
  94. _ = ec_file:remove(TmpDir, [recursive]),
  95. ?assertNot(filelib:is_dir(TmpDir)),
  96. ok = rebar_file_utils:reset_dir(TmpDir),
  97. ?assert(filelib:is_dir(TmpDir)),
  98. {ok, []} = rebar_utils:list_dir(TmpDir).
  99. reset_empty_dir(Config) ->
  100. TmpDir = ?config(tmpdir, Config),
  101. _ = ec_file:remove(TmpDir, [recursive]),
  102. _ = filelib:ensure_dir(filename:join([TmpDir, "dummy.beam"])),
  103. ?assert(filelib:is_dir(TmpDir)),
  104. ok = rebar_file_utils:reset_dir(TmpDir),
  105. ?assert(filelib:is_dir(TmpDir)),
  106. {ok, []} = rebar_utils:list_dir(TmpDir).
  107. reset_dir(Config) ->
  108. TmpDir = ?config(tmpdir, Config),
  109. _ = ec_file:remove(TmpDir, [recursive]),
  110. _ = filelib:ensure_dir(filename:join([TmpDir, "dummy.beam"])),
  111. ?assert(filelib:is_dir(TmpDir)),
  112. lists:foreach(fun(Name) -> file:write_file(filename:join([TmpDir, Name]), <<>>) end,
  113. ["a", "b", "c"]),
  114. lists:foreach(fun(File) -> ?assert(filelib:is_file(filename:join([TmpDir, File]))) end,
  115. ["a", "b", "c"]),
  116. ok = rebar_file_utils:reset_dir(TmpDir),
  117. ?assert(filelib:is_dir(TmpDir)),
  118. {ok, []} = rebar_utils:list_dir(TmpDir).
  119. path_from_ancestor(_Config) ->
  120. ?assertEqual({ok, "foo/bar/baz"}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/")),
  121. ?assertEqual({ok, "bar/baz"}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/foo")),
  122. ?assertEqual({ok, "bar"}, rebar_file_utils:path_from_ancestor("foo/bar", "foo")),
  123. ?assertEqual({ok, "bar"}, rebar_file_utils:path_from_ancestor("foo/bar/", "foo/")),
  124. ?assertEqual({error, badparent}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/qux")),
  125. ?assertEqual({error, badparent}, rebar_file_utils:path_from_ancestor("/foo/bar/baz", "/foo/bar/baz/qux")).
  126. canonical_path(_Config) ->
  127. %% We find the root so that the name works both on unix-likes and
  128. %% with Windows.
  129. Root = case os:type() of
  130. {win32, _} -> filename:nativename(filename:absname("/")); % C:\, with proper drive
  131. _ -> "/"
  132. end,
  133. ?assertEqual(filename:nativename(Root), rebar_file_utils:canonical_path("/")),
  134. ?assertEqual(filename:nativename(Root), rebar_file_utils:canonical_path("/../../..")),
  135. ?assertEqual(Root ++ "foo", rebar_file_utils:canonical_path("/foo/bar/..")),
  136. ?assertEqual(Root ++ "foo", rebar_file_utils:canonical_path("/foo/../foo")),
  137. ?assertEqual(Root ++ "foo", rebar_file_utils:canonical_path("/foo/.")),
  138. ?assertEqual(Root ++ "foo", rebar_file_utils:canonical_path("/foo/./.")),
  139. ?assertEqual(filename:nativename(Root ++ "foo/bar"),
  140. rebar_file_utils:canonical_path("/foo/./bar")).
  141. resolve_link(_Config) ->
  142. TmpDir = rebar_file_utils:system_tmpdir(
  143. ["rebar_file_utils_SUITE", "resolve_link"]),
  144. Link = filename:join(TmpDir, "link"),
  145. Target = filename:join(TmpDir, "link-target"),
  146. ec_file:remove(TmpDir, [recursive]),
  147. ok = filelib:ensure_dir(Target),
  148. ok = file:write_file(Target, <<>>),
  149. ok = file:make_symlink(Target, Link),
  150. ?assertEqual(Target, rebar_file_utils:resolve_link(Link)).
  151. split_dirname(_Config) ->
  152. ?assertEqual({".", ""}, rebar_file_utils:split_dirname("")),
  153. ?assertEqual({"/", ""}, rebar_file_utils:split_dirname("/")),
  154. ?assertEqual({"/", "foo"}, rebar_file_utils:split_dirname("/foo")),
  155. ?assertEqual({".", "foo"}, rebar_file_utils:split_dirname("foo")),
  156. ?assertEqual({"/foo", "bar"}, rebar_file_utils:split_dirname("/foo/bar")),
  157. ?assertEqual({"foo", "bar"}, rebar_file_utils:split_dirname("foo/bar")).
  158. mv_warning_is_ignored(_Config) ->
  159. meck:new(rebar_utils, [passthrough]),
  160. meck:expect(rebar_utils, sh, fun("mv ding dong", _) -> {ok, "Warning"} end),
  161. ok = rebar_file_utils:mv("ding", "dong"),
  162. meck:unload(rebar_utils).
  163. %%% Ensure Windows & Unix operations to move files
  164. mv_dir(Config) ->
  165. %% Move a directory to another one location
  166. PrivDir = ?config(priv_dir, Config),
  167. BaseDir = mk_base_dir(PrivDir, mv_dir),
  168. SrcDir = filename:join(BaseDir, "src/"),
  169. ec_file:mkdir_p(SrcDir),
  170. ?assert(filelib:is_dir(SrcDir)),
  171. %% empty dir movement
  172. DstDir1 = filename:join(BaseDir, "dst1/"),
  173. ?assertNot(filelib:is_dir(DstDir1)),
  174. ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir1)),
  175. ?assert(filelib:is_dir(DstDir1)),
  176. %% move files from dir to empty dir
  177. F1 = filename:join(SrcDir, "file1"),
  178. F2 = filename:join(SrcDir, "subdir/file2"),
  179. filelib:ensure_dir(F2),
  180. file:write_file(F1, "hello"),
  181. file:write_file(F2, "world"),
  182. DstDir2 = filename:join(BaseDir, "dst2/"),
  183. D2F1 = filename:join(DstDir2, "file1"),
  184. D2F2 = filename:join(DstDir2, "subdir/file2"),
  185. ?assertNot(filelib:is_dir(DstDir2)),
  186. ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir2)),
  187. ?assert(filelib:is_file(D2F1)),
  188. ?assert(filelib:is_file(D2F2)),
  189. %% move files from dir to existing dir moves it to
  190. %% a subdir
  191. filelib:ensure_dir(F2),
  192. file:write_file(F1, "hello"),
  193. file:write_file(F2, "world"),
  194. DstDir3 = filename:join(BaseDir, "dst3/"),
  195. D3F1 = filename:join(DstDir3, "src/file1"),
  196. D3F2 = filename:join(DstDir3, "src/subdir/file2"),
  197. ec_file:mkdir_p(DstDir3),
  198. ?assert(filelib:is_dir(DstDir3)),
  199. ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir3)),
  200. ?assertNot(filelib:is_file(F1)),
  201. ?assertNot(filelib:is_file(F2)),
  202. ?assert(filelib:is_file(D3F1)),
  203. ?assert(filelib:is_file(D3F2)),
  204. ?assertNot(filelib:is_dir(SrcDir)),
  205. ok.
  206. mv_file_same(Config) ->
  207. %% Move a file from a directory to the other without renaming
  208. PrivDir = ?config(priv_dir, Config),
  209. BaseDir = mk_base_dir(PrivDir, mv_file_same),
  210. SrcDir = filename:join(BaseDir, "src/"),
  211. ec_file:mkdir_p(SrcDir),
  212. ?assert(filelib:is_dir(SrcDir)),
  213. F = filename:join(SrcDir, "file"),
  214. file:write_file(F, "hello"),
  215. DstDir = filename:join(BaseDir, "dst/"),
  216. ec_file:mkdir_p(DstDir),
  217. Dst = filename:join(DstDir, "file"),
  218. ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
  219. ?assert(filelib:is_file(Dst)),
  220. ?assertNot(filelib:is_file(F)),
  221. ok.
  222. mv_file_diff(Config) ->
  223. %% Move a file from a directory to another one while renaming
  224. %% into a pre-existing file
  225. PrivDir = ?config(priv_dir, Config),
  226. BaseDir = mk_base_dir(PrivDir, mv_file_diff),
  227. SrcDir = filename:join(BaseDir, "src/"),
  228. ec_file:mkdir_p(SrcDir),
  229. ?assert(filelib:is_dir(SrcDir)),
  230. F = filename:join(SrcDir, "file"),
  231. file:write_file(F, "hello"),
  232. DstDir = filename:join(BaseDir, "dst/"),
  233. ec_file:mkdir_p(DstDir),
  234. Dst = filename:join(DstDir, "file-rename"),
  235. file:write_file(Dst, "not-the-right-content"),
  236. ?assert(filelib:is_file(Dst)),
  237. ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
  238. ?assert(filelib:is_file(Dst)),
  239. ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
  240. ?assertNot(filelib:is_file(F)),
  241. ok.
  242. mv_file_dir_same(Config) ->
  243. %% Move a file to a directory without renaming
  244. PrivDir = ?config(priv_dir, Config),
  245. BaseDir = mk_base_dir(PrivDir, mv_file_dir_same),
  246. SrcDir = filename:join(BaseDir, "src/"),
  247. ec_file:mkdir_p(SrcDir),
  248. ?assert(filelib:is_dir(SrcDir)),
  249. F = filename:join(SrcDir, "file"),
  250. file:write_file(F, "hello"),
  251. DstDir = filename:join(BaseDir, "dst/"),
  252. ec_file:mkdir_p(DstDir),
  253. Dst = filename:join(DstDir, "file"),
  254. ?assert(filelib:is_dir(DstDir)),
  255. ?assertEqual(ok, rebar_file_utils:mv(F, DstDir)),
  256. ?assert(filelib:is_file(Dst)),
  257. ?assertNot(filelib:is_file(F)),
  258. ok.
  259. mv_file_dir_diff(Config) ->
  260. %% Move a file to a directory while renaming
  261. PrivDir = ?config(priv_dir, Config),
  262. BaseDir = mk_base_dir(PrivDir, mv_file_dir_diff),
  263. SrcDir = filename:join(BaseDir, "src/"),
  264. ec_file:mkdir_p(SrcDir),
  265. ?assert(filelib:is_dir(SrcDir)),
  266. F = filename:join(SrcDir, "file"),
  267. file:write_file(F, "hello"),
  268. DstDir = filename:join(BaseDir, "dst/"),
  269. ec_file:mkdir_p(DstDir),
  270. Dst = filename:join(DstDir, "file-rename"),
  271. ?assert(filelib:is_dir(DstDir)),
  272. ?assertNot(filelib:is_file(Dst)),
  273. ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
  274. ?assert(filelib:is_file(Dst)),
  275. ?assertNot(filelib:is_file(F)),
  276. ok.
  277. mv_no_clobber(Config) ->
  278. %% Moving a file while renaming does not clobber other files
  279. PrivDir = ?config(priv_dir, Config),
  280. BaseDir = mk_base_dir(PrivDir, mv_no_clobber),
  281. SrcDir = filename:join(BaseDir, "src/"),
  282. ec_file:mkdir_p(SrcDir),
  283. ?assert(filelib:is_dir(SrcDir)),
  284. F = filename:join(SrcDir, "file"),
  285. file:write_file(F, "hello"),
  286. FBad = filename:join(SrcDir, "file-alt"),
  287. file:write_file(FBad, "wrong-data"),
  288. DstDir = filename:join(BaseDir, "dst/"),
  289. ec_file:mkdir_p(DstDir),
  290. Dst = filename:join(DstDir, "file-alt"),
  291. DstBad = filename:join(DstDir, "file"),
  292. file:write_file(DstBad, "wrong-data"),
  293. ?assert(filelib:is_file(F)),
  294. ?assert(filelib:is_file(FBad)),
  295. ?assert(filelib:is_dir(DstDir)),
  296. ?assertNot(filelib:is_file(Dst)),
  297. ?assert(filelib:is_file(DstBad)),
  298. ?assertEqual(ok, rebar_file_utils:mv(F, Dst)),
  299. ?assert(filelib:is_file(Dst)),
  300. ?assertNot(filelib:is_file(F)),
  301. ?assert(filelib:is_file(DstBad)),
  302. ?assert(filelib:is_file(FBad)),
  303. ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)),
  304. ?assertEqual({ok, <<"wrong-data">>}, file:read_file(FBad)),
  305. ?assertEqual({ok, <<"wrong-data">>}, file:read_file(DstBad)),
  306. ok.
  307. mk_base_dir(BasePath, Name) ->
  308. {_,_,Micro} = os:timestamp(),
  309. Index = integer_to_list(Micro),
  310. Path = filename:join(BasePath, atom_to_list(Name) ++ Index),
  311. ec_file:mkdir_p(Path),
  312. Path.
  313. symlink_existing_dir(Config) ->
  314. PrivDir = ?config(priv_dir, Config),
  315. Source = filename:join([PrivDir, "exists"]),
  316. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "exists"]),
  317. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  318. ok = filelib:ensure_dir(filename:join([Source, "dummy"])),
  319. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  320. %% `Target' should be a symlink, and `Source'
  321. %% should exist
  322. ?assert(ec_file:is_dir(Target)),
  323. ?assert(ec_file:is_symlink(Target)),
  324. ?assert(ec_file:is_dir(Source)),
  325. %% repeat to check for idempotence
  326. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  327. %% `Target' should be a symlink, and `Source'
  328. %% should exist
  329. ?assert(ec_file:is_dir(Target)),
  330. ?assert(ec_file:is_symlink(Target)),
  331. ?assert(ec_file:is_dir(Source)).
  332. preserve_existing_symlink(Config) ->
  333. PrivDir = ?config(priv_dir, Config),
  334. Source = filename:join([PrivDir, "symlinked"]),
  335. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "symlinked"]),
  336. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  337. ok = filelib:ensure_dir(filename:join([Source, "dummy"])),
  338. ok = rebar_file_utils:symlink(Source, Target),
  339. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  340. %% `Target' should be a symlink, and `Source'
  341. %% should exist
  342. ?assert(ec_file:is_dir(Target)),
  343. ?assert(ec_file:is_symlink(Target)),
  344. ?assert(ec_file:is_dir(Source)),
  345. %% repeat to check for idempotence
  346. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  347. %% `Target' should be a symlink, and `Source'
  348. %% should exist
  349. ?assert(ec_file:is_dir(Target)),
  350. ?assert(ec_file:is_symlink(Target)),
  351. ?assert(ec_file:is_dir(Source)).
  352. create_missing_dir(Config) ->
  353. PrivDir = ?config(priv_dir, Config),
  354. Source = filename:join([PrivDir, "missing"]),
  355. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "missing"]),
  356. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  357. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  358. %% `Target' should be a directory not a symlink, and `Source'
  359. %% should not exist
  360. ?assert(ec_file:is_dir(Target)),
  361. ?assert(not ec_file:is_symlink(Target)),
  362. ?assert(not ec_file:is_dir(Source)),
  363. %% repeat to check for idempotence
  364. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  365. %% `Target' should be a directory not a symlink, and `Source'
  366. %% should not exist
  367. ?assert(ec_file:is_dir(Target)),
  368. ?assert(not ec_file:is_symlink(Target)),
  369. ?assert(not ec_file:is_dir(Source)).
  370. preserve_existing_dir(Config) ->
  371. PrivDir = ?config(priv_dir, Config),
  372. Source = filename:join([PrivDir, "created"]),
  373. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "created"]),
  374. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  375. ok = filelib:ensure_dir(filename:join([Target, "dummy"])),
  376. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  377. %% `Target' should be a directory, and `Source'
  378. %% should not exist
  379. ?assert(ec_file:is_dir(Target)),
  380. ?assert(not ec_file:is_symlink(Target)),
  381. ?assert(not ec_file:is_dir(Source)),
  382. %% repeat to check for idempotence
  383. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  384. %% `Target' should be a directory, and `Source'
  385. %% should not exist
  386. ?assert(ec_file:is_dir(Target)),
  387. ?assert(not ec_file:is_symlink(Target)),
  388. ?assert(not ec_file:is_dir(Source)).
  389. delete_symlink_and_create_dir(Config) ->
  390. PrivDir = ?config(priv_dir, Config),
  391. Source = filename:join([PrivDir, "gone"]),
  392. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "gone"]),
  393. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  394. %% create dir temporarily to ensure symlink can be created
  395. ok = filelib:ensure_dir(filename:join([Source, "dummy"])),
  396. ok = rebar_file_utils:symlink(Source, Target),
  397. %% remove source dir
  398. ok = ec_file:remove(Source, [recursive]),
  399. ?assert(ec_file:is_symlink(Target)),
  400. ?assert(not ec_file:is_dir(Source)),
  401. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  402. %% `Target' should be a directory not a symlink, and `Source'
  403. %% should not exist
  404. ?assert(not ec_file:is_symlink(Target)),
  405. ?assert(ec_file:is_dir(Target)),
  406. ?assert(not ec_file:is_dir(Source)),
  407. %% repeat to check for idempotence
  408. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  409. %% `Target' should be a directory not a symlink, and `Source'
  410. %% should not exist
  411. ?assert(not ec_file:is_symlink(Target)),
  412. ?assert(ec_file:is_dir(Target)),
  413. ?assert(not ec_file:is_dir(Source)).
  414. delete_dir_and_create_symlink(Config) ->
  415. PrivDir = ?config(priv_dir, Config),
  416. Source = filename:join([PrivDir, "create"]),
  417. Target = filename:join([PrivDir, "_build", "test", "lib", "app", "create"]),
  418. ok = filelib:ensure_dir(filename:join([PrivDir, "_build", "test", "lib", "app", "dummy"])),
  419. ok = filelib:ensure_dir(filename:join([Source, "dummy"])),
  420. ok = filelib:ensure_dir(filename:join([Target, "dummy"])),
  421. ?assert(not ec_file:is_symlink(Target)),
  422. ?assert(ec_file:is_dir(Target)),
  423. ?assert(ec_file:is_dir(Source)),
  424. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  425. %% `Target' should be a symlink not a directory, and `Source'
  426. %% should exist
  427. ?assert(ec_file:is_dir(Target)),
  428. ?assert(ec_file:is_symlink(Target)),
  429. ?assert(ec_file:is_dir(Source)),
  430. %% repeat to check for idempotence
  431. ok = rebar_file_utils:symlink_or_create_dir(Source, Target),
  432. %% `Target' should be a symlink not a directory, and `Source'
  433. %% should exist
  434. ?assert(ec_file:is_dir(Target)),
  435. ?assert(ec_file:is_symlink(Target)),
  436. ?assert(ec_file:is_dir(Source)).