diff options
-rw-r--r-- | CONTRIBUTING.md | 6 | ||||
-rw-r--r-- | THANKS | 1 | ||||
-rw-r--r-- | src/rebar.hrl | 1 | ||||
-rw-r--r-- | src/rebar_base_compiler.erl | 24 | ||||
-rw-r--r-- | src/rebar_dialyzer_format.erl | 16 | ||||
-rw-r--r-- | src/rebar_dir.erl | 39 | ||||
-rw-r--r-- | src/rebar_file_utils.erl | 117 | ||||
-rw-r--r-- | src/rebar_prv_dialyzer.erl | 7 | ||||
-rw-r--r-- | test/rebar_file_utils_SUITE.erl | 190 |
9 files changed, 344 insertions, 57 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8077813..16de1e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -162,7 +162,7 @@ $ ./rebar3 ct ``` Most tests are named according to their module name followed by the `_SUITE` -suffi. Providers are made shorter, such that `rebar_prv_new` is tested in +suffix. Providers are made shorter, such that `rebar_prv_new` is tested in `rebar_new_SUITE`. Most tests in the test suite will rely on calling Rebar3 in its API form, @@ -285,7 +285,7 @@ The reviewer may ask you to later squash the commits together to provide a clean commit history before merging in the feature. It's important to write a proper commit title and description. The commit title -should fir around 50 characters; it is the first line of the commit text. The +should be no more than 50 characters; it is the first line of the commit text. The second line of the commit text must be left blank. The third line and beyond is the commit message. You should write a commit message. If you do, wrap all lines at 72 characters. You should explain what the commit does, what @@ -299,7 +299,7 @@ maintainers. When opening a pull request, explain what the patch is doing and if it makes sense, why the proposed implementation was chosen. Try to use well-defined commits (one feature per commit) so that reading -them and testing them is easier for reviewers and while bissecting the code +them and testing them is easier for reviewers and while bisecting the code base for issues. During the review process, you may be asked to correct or edit a few things @@ -138,3 +138,4 @@ Carlos Eduardo de Paula Derek Brown Heinz N. Gies Roberto Aloi +Andrew McRobb diff --git a/src/rebar.hrl b/src/rebar.hrl index c94a84a..ca44283 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -27,6 +27,7 @@ -define(REMOTE_PACKAGE_DIR, "tarballs"). -define(REMOTE_REGISTRY_FILE, "registry.ets.gz"). -define(LOCK_FILE, "rebar.lock"). +-define(DEFAULT_COMPILER_SOURCE_FORMAT, relative). -define(PACKAGE_INDEX_VERSION, 3). -define(PACKAGE_TABLE, package_index). diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index dcb1975..480e49c 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -35,7 +35,6 @@ error_tuple/4, format_error_source/2]). --define(DEFAULT_COMPILER_SOURCE_FORMAT, relative). -type desc() :: term(). -type loc() :: {line(), col()} | line(). -type line() :: integer(). @@ -138,28 +137,7 @@ error_tuple(Source, Es, Ws, Opts) -> -spec format_error_source(file:filename(), rebar_dict() | [{_,_}]) -> file:filename(). format_error_source(Path, Opts) -> - Type = case rebar_opts:get(Opts, compiler_source_format, - ?DEFAULT_COMPILER_SOURCE_FORMAT) of - V when V == absolute; V == relative; V == build -> - V; - Other -> - ?WARN("Invalid argument ~p for compiler_source_format - " - "assuming ~s~n", [Other, ?DEFAULT_COMPILER_SOURCE_FORMAT]), - ?DEFAULT_COMPILER_SOURCE_FORMAT - end, - case Type of - absolute -> resolve_linked_source(Path); - build -> Path; - relative -> - Cwd = rebar_dir:get_cwd(), - rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd) - end. - -%% @private takes a filename and canonicalizes its path if it is a link. --spec resolve_linked_source(file:filename()) -> file:filename(). -resolve_linked_source(Src) -> - {Dir, Base} = rebar_file_utils:split_dirname(Src), - filename:join(rebar_file_utils:resolve_link(Dir), Base). + rebar_dir:format_source_file_name(Path, Opts). %% =================================================================== %% Internal functions diff --git a/src/rebar_dialyzer_format.erl b/src/rebar_dialyzer_format.erl index b30c4dc..1d234c3 100644 --- a/src/rebar_dialyzer_format.erl +++ b/src/rebar_dialyzer_format.erl @@ -16,18 +16,19 @@ -include("rebar.hrl"). --export([format_warnings/1]). +-export([format_warnings/2]). %% Formats a list of warnings in a nice per file way. Note that we reverse %% the list at the end to 'undo' the reversal by foldl -format_warnings(Warnings) -> - {_, Res} = lists:foldl(fun format_warning_/2, {undefined, []}, Warnings), +format_warnings(Opts, Warnings) -> + Fold = fun(Warning, Acc) -> format_warning_(Opts, Warning, Acc) end, + {_, Res} = lists:foldl(Fold, {undefined, []}, Warnings), lists:reverse(Res). %% If the last seen file is and the file of this warning are the same %% we skip the file header -format_warning_(Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) -> +format_warning_(_Opts, Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) -> try String = message_to_string(Msg), {File, [lists:flatten(fmt("~!c~4w~!!: ~s", [Line, String])) | Acc]} @@ -39,8 +40,9 @@ format_warning_(Warning = {_Tag, {File, Line}, Msg}, {File, Acc}) -> end; %% With a new file detencted we also write a file header. -format_warning_(Warning = {_Tag, {File, Line}, Msg}, {_LastFile, Acc}) -> +format_warning_(Opts, Warning = {_Tag, {SrcFile, Line}, Msg}, {_LastFile, Acc}) -> try + File = rebar_dir:format_source_file_name(SrcFile, Opts), Base = filename:basename(File), Dir = filename:dirname(File), Root = filename:rootname(Base), @@ -49,12 +51,12 @@ format_warning_(Warning = {_Tag, {File, Line}, Msg}, {_LastFile, Acc}) -> Base1 = fmt("~!_c~s~!!~!__~s", [Root, Ext]), F = fmt("~!__~s", [filename:join(Path, Base1)]), String = message_to_string(Msg), - {File, [lists:flatten(fmt("~n~s~n~!c~4w~!!: ~s", [F, Line, String])) | Acc]} + {SrcFile, [lists:flatten(fmt("~n~s~n~!c~4w~!!: ~s", [F, Line, String])) | Acc]} catch Error:Reason -> ?DEBUG("Failed to pretty format warning: ~p:~p~n~p", [Error, Reason, erlang:get_stacktrace()]), - {File, [dialyzer:format_warning(Warning, fullpath) | Acc]} + {SrcFile, [dialyzer:format_warning(Warning, fullpath) | Acc]} end. fmt(Fmt) -> diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index 069d8fd..b61bfcc 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -26,7 +26,8 @@ src_dir_opts/2, recursive/2, extra_src_dirs/1, extra_src_dirs/2, all_src_dirs/1, all_src_dirs/3, - retarget_path/2]). + retarget_path/2, + format_source_file_name/2]). -include("rebar.hrl"). @@ -334,3 +335,39 @@ retarget_path(State, Path, [App|Rest]) -> {ok, NewPath} -> filename:join([rebar_app_info:out_dir(App), NewPath]); {error, badparent} -> retarget_path(State, Path, Rest) end. + +format_source_file_name(Path, Opts) -> + Type = case rebar_opts:get(Opts, compiler_source_format, + ?DEFAULT_COMPILER_SOURCE_FORMAT) of + V when V == absolute; V == relative; V == build -> + V; + Other -> + warn_source_format_once(Other) + end, + case Type of + absolute -> resolve_linked_source(Path); + build -> Path; + relative -> + Cwd = rebar_dir:get_cwd(), + rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd) + end. + +%% @private displays a warning for the compiler source format option +%% only once +-spec warn_source_format_once(term()) -> ok. +warn_source_format_once(Format) -> + Warn = application:get_env(rebar, warn_source_format) =/= {ok, false}, + application:set_env(rebar, warn_source_format, false), + case Warn of + false -> + ok; + true -> + ?WARN("Invalid argument ~p for compiler_source_format - " + "assuming ~s~n", [Format, ?DEFAULT_COMPILER_SOURCE_FORMAT]) + end. + +%% @private takes a filename and canonicalizes its path if it is a link. +-spec resolve_linked_source(file:filename()) -> file:filename(). +resolve_linked_source(Src) -> + {Dir, Base} = rebar_file_utils:split_dirname(Src), + filename:join(rebar_file_utils:resolve_link(Dir), Base). diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl index 8645641..8158312 100644 --- a/src/rebar_file_utils.erl +++ b/src/rebar_file_utils.erl @@ -185,32 +185,113 @@ mv(Source, Dest) -> ok end; {win32, _} -> - Cmd = case filelib:is_dir(Source) of - true -> - ?FMT("robocopy /move /e \"~s\" \"~s\" 1> nul", - [rebar_utils:escape_double_quotes(filename:nativename(Source)), - rebar_utils:escape_double_quotes(filename:nativename(Dest))]); - false -> - ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\" 1> nul", - [rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))), - rebar_utils:escape_double_quotes(filename:nativename(Dest)), - rebar_utils:escape_double_quotes(filename:basename(Source))]) - end, - Res = rebar_utils:sh(Cmd, - [{use_stdout, false}, return_on_error]), - case win32_ok(Res) of - true -> ok; + case filelib:is_dir(Source) of + true -> + SrcDir = filename:nativename(Source), + DestDir = case filelib:is_dir(Dest) of + true -> + %% to simulate unix/posix mv, we have to replicate + %% the same directory movement by moving the whole + %% top-level directory, not just the insides + SrcName = filename:basename(Source), + filename:nativename(filename:join(Dest, SrcName)); + false -> + filename:nativename(Dest) + end, + robocopy_dir(SrcDir, DestDir); false -> - {error, lists:flatten( - io_lib:format("Failed to move ~s to ~s~n", - [Source, Dest]))} + SrcDir = filename:nativename(filename:dirname(Source)), + SrcName = filename:basename(Source), + DestDir = filename:nativename(filename:dirname(Dest)), + DestName = filename:basename(Dest), + IsDestDir = filelib:is_dir(Dest), + if IsDestDir -> + %% if basename and target name are different because + %% we move to a directory, then just move there. + %% Similarly, if they are the same but we're going to + %% a directory, let's just do that directly. + FullDestDir = filename:nativename(Dest), + robocopy_file(SrcDir, FullDestDir, SrcName) + ; SrcName =:= DestName -> + %% if basename and target name are the same and both are files, + %% we do a regular move with robocopy without rename. + robocopy_file(SrcDir, DestDir, DestName) + ; SrcName =/= DestName-> + robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) + end + end end. +robocopy_mv_and_rename(Source, Dest, SrcDir, SrcName, DestDir, DestName) -> + %% If we're moving a file and the origin and + %% destination names are different: + %% - mktmp + %% - robocopy source_dir tmp_dir srcname + %% - rename srcname destname (to avoid clobbering) + %% - robocopy tmp_dir dest_dir destname + %% - remove tmp_dir + case ec_file:insecure_mkdtemp() of + {error, _Reason} -> + {error, lists:flatten( + io_lib:format("Failed to move ~s to ~s (tmpdir failed)~n", + [Source, Dest]))}; + TmpPath -> + case robocopy_file(SrcDir, TmpPath, SrcName) of + {error, Reason} -> + {error, Reason}; + ok -> + TmpSrc = filename:join(TmpPath, SrcName), + TmpDst = filename:join(TmpPath, DestName), + case file:rename(TmpSrc, TmpDst) of + {error, _} -> + {error, lists:flatten( + io_lib:format("Failed to move ~s to ~s (via rename)~n", + [Source, Dest]))}; + ok -> + case robocopy_file(TmpPath, DestDir, DestName) of + Err = {error, _} -> Err; + OK -> rm_rf(TmpPath), OK + end + end + end + end. + +robocopy_file(SrcPath, DestPath, FileName) -> + Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\"", + [rebar_utils:escape_double_quotes(SrcPath), + rebar_utils:escape_double_quotes(DestPath), + rebar_utils:escape_double_quotes(FileName)]), + Res = rebar_utils:sh(Cmd, [{use_stdout, false}, return_on_error]), + case win32_ok(Res) of + false -> + {error, lists:flatten( + io_lib:format("Failed to move ~s to ~s~n", + [filename:join(SrcPath, FileName), + filename:join(DestPath, FileName)]))}; + true -> + ok + end. + +robocopy_dir(Source, Dest) -> + Cmd = ?FMT("robocopy /move /e \"~s\" \"~s\"", + [rebar_utils:escape_double_quotes(Source), + rebar_utils:escape_double_quotes(Dest)]), + Res = rebar_utils:sh(Cmd, + [{use_stdout, false}, return_on_error]), + case win32_ok(Res) of + true -> ok; + false -> + {error, lists:flatten( + io_lib:format("Failed to move ~s to ~s~n", + [Source, Dest]))} + end. + win32_ok({ok, _}) -> true; win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true; win32_ok(_) -> false. + delete_each([]) -> ok; delete_each([File | Rest]) -> diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 44dc0d2..21d7f5a 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -478,7 +478,8 @@ run_dialyzer(State, Opts, Output) -> {check_plt, false} | Opts], ?DEBUG("Running dialyzer with options: ~p~n", [Opts2]), - Warnings = format_warnings(Output, dialyzer:run(Opts2)), + Warnings = format_warnings(rebar_state:opts(State), + Output, dialyzer:run(Opts2)), {Warnings, State}; false -> Opts2 = [{warnings, no_warnings()}, @@ -497,8 +498,8 @@ legacy_warnings(Warnings) -> Warnings end. -format_warnings(Output, Warnings) -> - Warnings1 = rebar_dialyzer_format:format_warnings(Warnings), +format_warnings(Opts, Output, Warnings) -> + Warnings1 = rebar_dialyzer_format:format_warnings(Opts, Warnings), console_warnings(Warnings1), file_warnings(Output, Warnings), length(Warnings). diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl index 7285e13..4cc6a93 100644 --- a/test/rebar_file_utils_SUITE.erl +++ b/test/rebar_file_utils_SUITE.erl @@ -4,6 +4,8 @@ groups/0, init_per_group/2, end_per_group/2, + init_per_testcase/2, + end_per_testcase/2, raw_tmpdir/1, empty_tmpdir/1, simple_tmpdir/1, @@ -15,7 +17,13 @@ canonical_path/1, resolve_link/1, split_dirname/1, - mv_warning_is_ignored/1]). + mv_warning_is_ignored/1, + mv_dir/1, + mv_file_same/1, + mv_file_diff/1, + mv_file_dir_same/1, + mv_file_dir_diff/1, + mv_no_clobber/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -25,6 +33,7 @@ all() -> [{group, tmpdir}, {group, reset_dir}, + {group, mv}, path_from_ancestor, canonical_path, resolve_link, @@ -33,7 +42,9 @@ all() -> groups() -> [{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]}, - {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]}]. + {reset_dir, [], [reset_nonexistent_dir, reset_empty_dir, reset_dir]}, + {mv, [], [mv_dir, mv_file_same, mv_file_diff, + mv_file_dir_same, mv_file_dir_diff, mv_no_clobber]}]. init_per_group(reset_dir, Config) -> TmpDir = rebar_file_utils:system_tmpdir(["rebar_file_utils_SUITE", "resetable"]), @@ -41,6 +52,20 @@ init_per_group(reset_dir, Config) -> init_per_group(_, Config) -> Config. end_per_group(_, Config) -> Config. +init_per_testcase(Test, Config) -> + case os:type() of + {win32, _} -> + case lists:member(Test, [resolve_link, mv_warning_is_ignored]) of + true -> {skip, "broken in windows"}; + false -> Config + end; + _ -> + Config + end. + +end_per_testcase(_Test, Config) -> + Config. + raw_tmpdir(_Config) -> case rebar_file_utils:system_tmpdir() of "/tmp" -> ok; @@ -143,3 +168,164 @@ mv_warning_is_ignored(_Config) -> meck:expect(rebar_utils, sh, fun("mv ding dong", _) -> {ok, "Warning"} end), ok = rebar_file_utils:mv("ding", "dong"), meck:unload(rebar_utils). + +%%% Ensure Windows & Unix operations to move files + +mv_dir(Config) -> + %% Move a directory to another one location + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_dir), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + %% empty dir movement + DstDir1 = filename:join(BaseDir, "dst1/"), + ?assertNot(filelib:is_dir(DstDir1)), + ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir1)), + ?assert(filelib:is_dir(DstDir1)), + + %% move files from dir to empty dir + F1 = filename:join(SrcDir, "file1"), + F2 = filename:join(SrcDir, "subdir/file2"), + filelib:ensure_dir(F2), + file:write_file(F1, "hello"), + file:write_file(F2, "world"), + DstDir2 = filename:join(BaseDir, "dst2/"), + D2F1 = filename:join(DstDir2, "file1"), + D2F2 = filename:join(DstDir2, "subdir/file2"), + ?assertNot(filelib:is_dir(DstDir2)), + ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir2)), + ?assert(filelib:is_file(D2F1)), + ?assert(filelib:is_file(D2F2)), + + %% move files from dir to existing dir moves it to + %% a subdir + filelib:ensure_dir(F2), + file:write_file(F1, "hello"), + file:write_file(F2, "world"), + DstDir3 = filename:join(BaseDir, "dst3/"), + D3F1 = filename:join(DstDir3, "src/file1"), + D3F2 = filename:join(DstDir3, "src/subdir/file2"), + ec_file:mkdir_p(DstDir3), + ?assert(filelib:is_dir(DstDir3)), + ?assertEqual(ok, rebar_file_utils:mv(SrcDir, DstDir3)), + ?assertNot(filelib:is_file(F1)), + ?assertNot(filelib:is_file(F2)), + ?assert(filelib:is_file(D3F1)), + ?assert(filelib:is_file(D3F2)), + ?assertNot(filelib:is_dir(SrcDir)), + ok. + +mv_file_same(Config) -> + %% Move a file from a directory to the other without renaming + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_file_same), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + F = filename:join(SrcDir, "file"), + file:write_file(F, "hello"), + DstDir = filename:join(BaseDir, "dst/"), + ec_file:mkdir_p(DstDir), + Dst = filename:join(DstDir, "file"), + ?assertEqual(ok, rebar_file_utils:mv(F, Dst)), + ?assert(filelib:is_file(Dst)), + ?assertNot(filelib:is_file(F)), + ok. + +mv_file_diff(Config) -> + %% Move a file from a directory to another one while renaming + %% into a pre-existing file + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_file_diff), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + F = filename:join(SrcDir, "file"), + file:write_file(F, "hello"), + DstDir = filename:join(BaseDir, "dst/"), + ec_file:mkdir_p(DstDir), + Dst = filename:join(DstDir, "file-rename"), + file:write_file(Dst, "not-the-right-content"), + ?assert(filelib:is_file(Dst)), + ?assertEqual(ok, rebar_file_utils:mv(F, Dst)), + ?assert(filelib:is_file(Dst)), + ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)), + ?assertNot(filelib:is_file(F)), + ok. + +mv_file_dir_same(Config) -> + %% Move a file to a directory without renaming + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_file_dir_same), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + F = filename:join(SrcDir, "file"), + file:write_file(F, "hello"), + DstDir = filename:join(BaseDir, "dst/"), + ec_file:mkdir_p(DstDir), + Dst = filename:join(DstDir, "file"), + ?assert(filelib:is_dir(DstDir)), + ?assertEqual(ok, rebar_file_utils:mv(F, DstDir)), + ?assert(filelib:is_file(Dst)), + ?assertNot(filelib:is_file(F)), + ok. + +mv_file_dir_diff(Config) -> + %% Move a file to a directory while renaming + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_file_dir_diff), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + F = filename:join(SrcDir, "file"), + file:write_file(F, "hello"), + DstDir = filename:join(BaseDir, "dst/"), + ec_file:mkdir_p(DstDir), + Dst = filename:join(DstDir, "file-rename"), + ?assert(filelib:is_dir(DstDir)), + ?assertNot(filelib:is_file(Dst)), + ?assertEqual(ok, rebar_file_utils:mv(F, Dst)), + ?assert(filelib:is_file(Dst)), + ?assertNot(filelib:is_file(F)), + ok. + +mv_no_clobber(Config) -> + %% Moving a file while renaming does not clobber other files + PrivDir = ?config(priv_dir, Config), + BaseDir = mk_base_dir(PrivDir, mv_no_clobber), + SrcDir = filename:join(BaseDir, "src/"), + ec_file:mkdir_p(SrcDir), + ?assert(filelib:is_dir(SrcDir)), + F = filename:join(SrcDir, "file"), + file:write_file(F, "hello"), + FBad = filename:join(SrcDir, "file-alt"), + file:write_file(FBad, "wrong-data"), + DstDir = filename:join(BaseDir, "dst/"), + ec_file:mkdir_p(DstDir), + Dst = filename:join(DstDir, "file-alt"), + DstBad = filename:join(DstDir, "file"), + file:write_file(DstBad, "wrong-data"), + ?assert(filelib:is_file(F)), + ?assert(filelib:is_file(FBad)), + ?assert(filelib:is_dir(DstDir)), + ?assertNot(filelib:is_file(Dst)), + ?assert(filelib:is_file(DstBad)), + ?assertEqual(ok, rebar_file_utils:mv(F, Dst)), + ?assert(filelib:is_file(Dst)), + ?assertNot(filelib:is_file(F)), + ?assert(filelib:is_file(DstBad)), + ?assert(filelib:is_file(FBad)), + ?assertEqual({ok, <<"hello">>}, file:read_file(Dst)), + ?assertEqual({ok, <<"wrong-data">>}, file:read_file(FBad)), + ?assertEqual({ok, <<"wrong-data">>}, file:read_file(DstBad)), + ok. + + +mk_base_dir(BasePath, Name) -> + {_,_,Micro} = os:timestamp(), + Index = integer_to_list(Micro), + Path = filename:join(BasePath, atom_to_list(Name) ++ Index), + ec_file:mkdir_p(Path), + Path. |