|
|
@@ -0,0 +1,159 @@
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+%% Copyright (c) 2020-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
|
+%%
|
|
|
+%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+%% you may not use this file except in compliance with the License.
|
|
|
+%% You may obtain a copy of the License at
|
|
|
+%%
|
|
|
+%% http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+%%
|
|
|
+%% Unless required by applicable law or agreed to in writing, software
|
|
|
+%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+%% See the License for the specific language governing permissions and
|
|
|
+%% limitations under the License.
|
|
|
+%%--------------------------------------------------------------------
|
|
|
+
|
|
|
+-module(emqx_ft_fs_util_SUITE).
|
|
|
+
|
|
|
+-compile(export_all).
|
|
|
+-compile(nowarn_export_all).
|
|
|
+
|
|
|
+-include_lib("common_test/include/ct.hrl").
|
|
|
+-include_lib("stdlib/include/assert.hrl").
|
|
|
+-include_lib("kernel/include/file.hrl").
|
|
|
+
|
|
|
+all() ->
|
|
|
+ emqx_common_test_helpers:all(?MODULE).
|
|
|
+
|
|
|
+t_fold_single_level(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a", #file_info{type = directory}, ["a"]},
|
|
|
+ {"c", #file_info{type = directory}, ["c"]},
|
|
|
+ {"d", #file_info{type = directory}, ["d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_multi_level(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b/foo/42", #file_info{type = regular}, ["42", "foo", "b", "a"]},
|
|
|
+ {"a/b/foo/Я", #file_info{type = regular}, ["Я", "foo", "b", "a"]},
|
|
|
+ {"d/e/baz/needle", #file_info{type = regular}, ["needle", "baz", "e", "d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*', '*']))
|
|
|
+ ),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b/foo", #file_info{type = directory}, ["foo", "b", "a"]},
|
|
|
+ {"c/bar/中文", #file_info{type = regular}, ["中文", "bar", "c"]},
|
|
|
+ {"d/e/baz", #file_info{type = directory}, ["baz", "e", "d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_no_glob(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [{"", #file_info{type = directory}, []}],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, []))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_glob_too_deep(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*', '*', '*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_invalid_root(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], filename:join([Root, "a", "link"]), ['*']))
|
|
|
+ ),
|
|
|
+ ?assertMatch(
|
|
|
+ [],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], filename:join([Root, "d", "haystack"]), ['*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_filter_unicode(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b/foo/42", #file_info{type = regular}, ["42", "foo", "b", "a"]},
|
|
|
+ {"d/e/baz/needle", #file_info{type = regular}, ["needle", "baz", "e", "d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*', fun is_latin1/1]))
|
|
|
+ ),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b/foo/Я", #file_info{type = regular}, ["Я", "foo", "b", "a"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*', is_not(fun is_latin1/1)]))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_filter_levels(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b/foo", #file_info{type = directory}, ["foo", "b", "a"]},
|
|
|
+ {"d/e/baz", #file_info{type = directory}, ["baz", "e", "d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, [fun is_letter/1, fun is_letter/1, '*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+t_fold_errors(Config) ->
|
|
|
+ Root = ?config(data_dir, Config),
|
|
|
+ ok = meck:new(emqx_ft_fs_util, [passthrough]),
|
|
|
+ ok = meck:expect(emqx_ft_fs_util, read_info, fun(AbsFilepath) ->
|
|
|
+ ct:pal("read_info(~p)", [AbsFilepath]),
|
|
|
+ Filename = filename:basename(AbsFilepath),
|
|
|
+ case Filename of
|
|
|
+ "b" -> {error, eacces};
|
|
|
+ "link" -> {error, enotsup};
|
|
|
+ "bar" -> {error, enotdir};
|
|
|
+ "needle" -> {error, ebusy};
|
|
|
+ _ -> meck:passthrough([AbsFilepath])
|
|
|
+ end
|
|
|
+ end),
|
|
|
+ ?assertMatch(
|
|
|
+ [
|
|
|
+ {"a/b", {error, eacces}, ["b", "a"]},
|
|
|
+ {"a/link", {error, enotsup}, ["link", "a"]},
|
|
|
+ {"c/link", {error, enotsup}, ["link", "c"]},
|
|
|
+ {"d/e/baz/needle", {error, ebusy}, ["needle", "baz", "e", "d"]}
|
|
|
+ ],
|
|
|
+ sort(emqx_ft_fs_util:fold(fun cons/4, [], Root, ['*', '*', '*', '*']))
|
|
|
+ ).
|
|
|
+
|
|
|
+%%
|
|
|
+
|
|
|
+is_not(F) ->
|
|
|
+ fun(X) -> not F(X) end.
|
|
|
+
|
|
|
+is_latin1(Filename) ->
|
|
|
+ case unicode:characters_to_binary(Filename, unicode, latin1) of
|
|
|
+ {error, _, _} ->
|
|
|
+ false;
|
|
|
+ _ ->
|
|
|
+ true
|
|
|
+ end.
|
|
|
+
|
|
|
+is_letter(Filename) ->
|
|
|
+ case Filename of
|
|
|
+ [_] ->
|
|
|
+ true;
|
|
|
+ _ ->
|
|
|
+ false
|
|
|
+ end.
|
|
|
+
|
|
|
+cons(Path, Info, Stack, Acc) ->
|
|
|
+ [{Path, Info, Stack} | Acc].
|
|
|
+
|
|
|
+sort(L) when is_list(L) ->
|
|
|
+ lists:sort(L).
|