diff --git a/interfaces/test/unittest/BUILD.gn b/interfaces/test/unittest/BUILD.gn index 9a3e2915ec3f109a57fc3638b5b85cec8f2c784f..f2b24ee613000359d5f05595e2af6296bf9b5d70 100644 --- a/interfaces/test/unittest/BUILD.gn +++ b/interfaces/test/unittest/BUILD.gn @@ -26,6 +26,7 @@ group("file_api_unittest") { "js:ani_file_securitylabel_test", "js:ani_file_statvfs_test", "js:napi_file_fs_test", + "movedir:movedir_test", "remote_uri:remote_uri_test", "task_signal:task_signal_test", ] diff --git a/interfaces/test/unittest/movedir/BUILD.gn b/interfaces/test/unittest/movedir/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..81668e05e6a68aa0adf550cba91b509db2a27b53 --- /dev/null +++ b/interfaces/test/unittest/movedir/BUILD.gn @@ -0,0 +1,57 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# 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. + +import("//build/test.gni") +import("//foundation/filemanagement/file_api/file_api.gni") + +ohos_unittest("movedir_test") { + module_out_path = "file_api/file_api" + + sources = [ + "${file_api_path}/interfaces/kits/js/src/mod_fs/properties/movedir.cpp", + "${file_api_path}/interfaces/test/unittest/movedir/mock/movedir_mock.cpp", + "${file_api_path}/interfaces/test/unittest/movedir/movedir_mock_test.cpp", + ] + + include_dirs = [ + "${file_api_path}/interfaces/kits/js/src/common/file_helper", + "${file_api_path}/interfaces/kits/js/src/mod_fs", + "${file_api_path}/interfaces/kits/js/src/mod_fs/properties", + "${file_api_path}/interfaces/test/unittest/movedir/mock", + ] + + use_exceptions = true + + deps = [ + "${file_api_path}/interfaces/kits/js:fs", + "${utils_path}/filemgmt_libhilog:filemgmt_libhilog", + "${utils_path}/filemgmt_libn:filemgmt_libn", + ] + + external_deps = [ + "ability_base:zuri", + "app_file_service:fileuri_native", + "c_utils:utils", + "c_utils:utilsbase", + "googletest:gmock_main", + "googletest:gtest_main", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_single", + "napi:ace_napi", + ] + + defines = [ + "private=public", + ] +} \ No newline at end of file diff --git a/interfaces/test/unittest/movedir/mock/movedir_mock.cpp b/interfaces/test/unittest/movedir/mock/movedir_mock.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9bd62a65e3b5fc5c6daaba54af1ebab088dffbd2 --- /dev/null +++ b/interfaces/test/unittest/movedir/mock/movedir_mock.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#include "movedir_mock.h" + +#include + +namespace OHOS::FileManagement::ModuleFileIO::Test { +using namespace OHOS::FileManagement::ModuleFileIO; + +thread_local std::shared_ptr MovedirMock::movedirMock = nullptr; +thread_local bool MovedirMock::mockable = false; + +std::shared_ptr MovedirMock::GetMock() +{ + if (movedirMock == nullptr) { + movedirMock = std::make_shared(); + } + return movedirMock; +} + +void MovedirMock::EnableMock() +{ + mockable = true; +} + +void MovedirMock::DisableMock() +{ + movedirMock = nullptr; + mockable = false; +} + +bool MovedirMock::IsMockable() +{ + return mockable; +} + +} // namespace OHOS::FileManagement::ModuleFileIO::Test + +#ifdef __cplusplus +using namespace OHOS::FileManagement::ModuleFileIO::Test; + +extern "C" { +uintmax_t _ZNSt3__h4__fs10filesystem12__remove_allERKNS1_4pathEPNS_10error_codeE( + const std::filesystem::path &p, std::error_code &ec) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->remove_all(p, ec); + } + + static uintmax_t (*realRemove)(const path &, std::error_code &) = []() { + auto func = (uintmax_t(*)(const path &, std::error_code &))dlsym(RTLD_NEXT, "filesystem::remove_all"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real remove: " << dlerror(); + } + return func; + }(); + + if (!realRemove) { + return -1; + } + + return realRemove(p, ec); +} + +int stat(const char *path, struct stat *buf) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->stat(path, buf); + } + + static int (*realStat)(const char *, struct stat *) = []() { + auto func = (int (*)(const char *, struct stat *))dlsym(RTLD_NEXT, "stat"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real stat: " << dlerror(); + } + return func; + }(); + + if (!realStat) { + return 0; + } + + return realStat(path, buf); +} +} + +napi_status napi_create_array(napi_env env, napi_value *result) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->napi_create_array(env, result); + } + + static napi_status (*realNapiCreateArray)(napi_env, napi_value *) = []() { + auto func = (napi_status(*)(napi_env, napi_value *))dlsym(RTLD_NEXT, "napi_create_array"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real napi_create_array: " << dlerror(); + } + return func; + }(); + + if (!realNapiCreateArray) { + return napi_ok; + } + + return realNapiCreateArray(env, result); +} + +std::filesystem::file_status status(const path &p, std::error_code &ec) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->status(p, ec); + } + + static std::filesystem::file_status (*realStatus)(const path &, std::error_code &) = []() { + auto func = (std::filesystem::file_status(*)(const path &, std::error_code &))dlsym(RTLD_NEXT, "status"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real status: " << dlerror(); + } + return func; + }(); + + if (!realStatus) { + std::filesystem::file_status default_status; + return default_status; + } + + return realStatus(p, ec); +} + +bool NFuncArg::InitArgs(std::function argcChecker) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->InitArgs(argcChecker); + } + + static bool (*realInitArgs)(std::function) = []() { + auto func = (bool (*)(std::function))dlsym(RTLD_NEXT, "InitArgs"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real InitArgs: " << dlerror(); + } + return func; + }(); + + if (!realInitArgs) { + return false; + } + + return realInitArgs(argcChecker); +} + +void NError::ThrowErr(napi_env env) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->ThrowErr(env); + } + + static void (*realThrowErr)(napi_env) = []() { + auto func = (void (*)(napi_env))dlsym(RTLD_NEXT, "ThrowErr"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real ThrowErr: " << dlerror(); + } + return func; + }(); + + if (!realThrowErr) { + return; + } + + return realThrowErr(env); +} + +void NError::ThrowErrAddData(napi_env env, int errCode, napi_value data) +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->ThrowErrAddData(env, errCode, data); + } + + static void (*realThrowErrAddData)(napi_env, int, napi_value) = []() { + auto func = (void (*)(napi_env, int, napi_value))dlsym(RTLD_NEXT, "ThrowErrAddData"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real ThrowErrAddData: " << dlerror(); + } + return func; + }(); + + if (!realThrowErrAddData) { + return; + } + + return realThrowErrAddData(env, errCode, data); +} + +std::tuple, size_t> NVal::ToUTF8StringPath() const +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->ToUTF8StringPath(); + } + + static std::tuple, size_t> (*realToUTF8StringPath)() = []() { + auto func = (std::tuple, size_t>(*)())dlsym(RTLD_NEXT, "ToUTF8StringPath"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real ToUTF8StringPath: " << dlerror(); + } + return func; + }(); + + if (!realToUTF8StringPath) { + return { false, nullptr, -1 }; + } + + return realToUTF8StringPath(); +} + +std::tuple NVal::ToInt32(int32_t defaultValue) const +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->ToInt32(defaultValue); + } + + static std::tuple (*realToInt32)(int32_t) = []() { + auto func = (std::tuple(*)(int32_t))dlsym(RTLD_NEXT, "ToInt32"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real ToInt32: " << dlerror(); + } + return func; + }(); + + if (!realToInt32) { + return { false, -1 }; + } + + return realToInt32(defaultValue); +} + +size_t NFuncArg::GetArgc(void) const +{ + if (MovedirMock::IsMockable()) { + return MovedirMock::GetMock()->GetArgc(); + } + + static size_t (*realGetArgc)() = []() { + auto func = (size_t(*)())dlsym(RTLD_NEXT, "GetArgc"); + if (!func) { + GTEST_LOG_(ERROR) << "Failed to resolve real GetArgc: " << dlerror(); + } + return func; + }(); + + if (!realGetArgc) { + return -1; + } + + return realGetArgc(); +} +#endif \ No newline at end of file diff --git a/interfaces/test/unittest/movedir/mock/movedir_mock.h b/interfaces/test/unittest/movedir/mock/movedir_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..854479f7644b460339b7f35753756856aa1f0993 --- /dev/null +++ b/interfaces/test/unittest/movedir/mock/movedir_mock.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef INTERFACES_TEST_UNITTEST_MOVEDIR_MOCK_ATOMICFILE_MOCK_H +#define INTERFACES_TEST_UNITTEST_MOVEDIR_MOCK_ATOMICFILE_MOCK_H + +#include "n_napi.h" +#include "filemgmt_libn.h" +#include "movedir.h" + +#include +#include +#include +#include + +namespace OHOS::FileManagement::ModuleFileIO::Test { +using namespace std::filesystem; +using namespace OHOS::FileManagement::ModuleFileIO; +using namespace OHOS::FileManagement::LibN; + +class IMovedirMock { +public: + virtual ~IMovedirMock() = default; + virtual bool InitArgs(std::function argcChecker) = 0; + virtual std::tuple, size_t> ToUTF8StringPath() = 0; + virtual std::tuple ToInt32(int32_t defaultValue) = 0; + virtual size_t GetArgc(void) = 0; + virtual void ThrowErrAddData(napi_env env, int errCode, napi_value data) = 0; + virtual napi_status napi_create_array(napi_env env, napi_value *result) = 0; + virtual uintmax_t remove_all(const path &p, std::error_code &ec) = 0; + virtual file_status status(const path &p, std::error_code &ec) = 0; + virtual void ThrowErr(napi_env env) = 0; + virtual int stat(const char *path, struct stat *buf) = 0; +}; + +class MovedirMock : public IMovedirMock { +public: + MOCK_METHOD(int, stat, (const char *, struct stat *), (override)); + MOCK_METHOD(bool, InitArgs, (std::function), (override)); + MOCK_METHOD((std::tuple, size_t>), ToUTF8StringPath, (), (override)); + MOCK_METHOD((std::tuple), ToInt32, (int32_t), (override)); + MOCK_METHOD(void, ThrowErrAddData, (napi_env, int, napi_value), (override)); + MOCK_METHOD(size_t, GetArgc, (), (override)); + MOCK_METHOD(void, ThrowErr, (napi_env), (override)); + MOCK_METHOD(napi_status, napi_create_array, (napi_env, napi_value *), (override)); + MOCK_METHOD(uintmax_t, remove_all, (const path &, std::error_code &), (override)); + MOCK_METHOD(file_status, status, (const path &, std::error_code &), (override)); + +public: + static std::shared_ptr GetMock(); + static void EnableMock(); + static void DisableMock(); + static bool IsMockable(); + +private: + static thread_local std::shared_ptr movedirMock; + static thread_local bool mockable; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO::Test +#endif // INTERFACES_TEST_UNITTEST_CLASS_ATOMICFILE_MOCK_ATOMICFILE_MOCK_H \ No newline at end of file diff --git a/interfaces/test/unittest/movedir/movedir_mock_test.cpp b/interfaces/test/unittest/movedir/movedir_mock_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4368e4609484b7efe85df59d2d0b0fba36a1c297 --- /dev/null +++ b/interfaces/test/unittest/movedir/movedir_mock_test.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * 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. + */ + +#include "movedir_mock.h" + +#include +#include +#include + +#include "securec.h" + +#define MY_ERROR_VALUE 128 + +namespace OHOS { +namespace FileManagement { +namespace ModuleFileIO { +namespace Test { +using namespace std; +using namespace std::filesystem; +using namespace OHOS::FileManagement::ModuleFileIO; + +static const string g_movedirSrc = "/data/test/MovedirMockTestSrc1"; +static const string g_movedirDest = "/data/test/MovedirMockTestDest1"; + +class MovedirMockTest : public testing::Test { +public: + static void SetUpTestCase(void) + { + MovedirMock::EnableMock(); + }; + static void TearDownTestCase() + { + MovedirMock::DisableMock(); + }; + void SetUp() {}; + void TearDown() {}; +}; + +static std::tuple, size_t> JudgePath(const std::string &path) +{ + size_t srcLen = std::strlen(path.c_str()) + 1; + std::unique_ptr srcPtr = std::make_unique(srcLen); + auto ret = strncpy_s(srcPtr.get(), srcLen, path.c_str(), srcLen - 1); + srcPtr[srcLen - 1] = '\0'; + return { true, std::move(srcPtr), srcLen }; +} + +/** + * @tc.name: RmDirectory_0001 + * @tc.desc: Test function of RmDirectory interface. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + */ +HWTEST_F(MovedirMockTest, RmDirectory_0001, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "MovedirMockTest-begin RmDirectory_0001"; + napi_env env = reinterpret_cast(0x1000); + napi_callback_info info = reinterpret_cast(0x1000); + error_code myEcRemove(MY_ERROR_VALUE, generic_category()); + filesystem::path myPath(g_movedirSrc); + tuple myMode(true, DIRMODE_DIRECTORY_REPLACE); + auto mock_ = MovedirMock::GetMock(); + + auto statMock = [](const char *path, struct stat *buf) { + buf->st_mode = S_IFDIR | 0755; + return 0; + }; + + testing::Sequence s; + EXPECT_CALL(*mock_, InitArgs(testing::_)).WillOnce(testing::Return(true)); + EXPECT_CALL(*mock_, ToUTF8StringPath()) + .WillOnce(testing::Return(JudgePath(g_movedirSrc))) + .WillOnce(testing::Return(JudgePath(g_movedirDest))); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)).Times(5).InSequence(s).WillRepeatedly(testing::Invoke(statMock)); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)).InSequence(s).WillOnce(testing::Return(-1)); + EXPECT_CALL(*mock_, GetArgc()).WillOnce(testing::Return(NARG_CNT::THREE)); + EXPECT_CALL(*mock_, ToInt32(testing::_)).WillOnce(testing::Return(myMode)); + EXPECT_CALL(*mock_, remove_all(testing::_, testing::_)) + .WillOnce(testing::Invoke([](const path &p, std::error_code &ec) { + ec.assign(1, std::system_category()); + return 0; + })); + EXPECT_CALL(*mock_, ThrowErr(testing::_)); + + auto res = MoveDir::Sync(env, info); + EXPECT_EQ(res, nullptr); + + GTEST_LOG_(INFO) << "MovedirMockTest-end RmDirectory_0001"; +} + +/** + * @tc.name: RmDirectory_0002 + * @tc.desc: Test function of RmDirectory interface. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + */ +HWTEST_F(MovedirMockTest, RmDirectory_0002, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "MovedirMockTest-begin RmDirectory_0002"; + napi_env env = reinterpret_cast(0x1000); + napi_callback_info info = reinterpret_cast(0x1000); + error_code myEcRemove(MY_ERROR_VALUE, generic_category()); + filesystem::path myPath(g_movedirSrc); + tuple myMode(true, DIRMODE_DIRECTORY_REPLACE); + auto mock_ = MovedirMock::GetMock(); + + auto statMock = [](const char *path, struct stat *buf) { + buf->st_mode = S_IFDIR | 0755; + return 0; + }; + + testing::Sequence s; + EXPECT_CALL(*mock_, InitArgs(testing::_)).WillOnce(testing::Return(true)); + EXPECT_CALL(*mock_, ToUTF8StringPath()) + .WillOnce(testing::Return(JudgePath(g_movedirSrc))) + .WillOnce(testing::Return(JudgePath(g_movedirDest))); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)).Times(5).InSequence(s).WillRepeatedly(testing::Invoke(statMock)); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)).InSequence(s).WillOnce(testing::Return(-1)); + EXPECT_CALL(*mock_, GetArgc()).WillOnce(testing::Return(NARG_CNT::THREE)); + EXPECT_CALL(*mock_, ToInt32(testing::_)).WillOnce(testing::Return(myMode)); + EXPECT_CALL(*mock_, remove_all(testing::_, testing::_)) + .WillOnce(testing::Invoke([](const path &p, std::error_code &ec) { + ec.assign(0, std::system_category()); + return 0; + })); + EXPECT_CALL(*mock_, ThrowErr(testing::_)); + + auto res = MoveDir::Sync(env, info); + EXPECT_EQ(res, nullptr); + + GTEST_LOG_(INFO) << "MovedirMockTest-end RmDirectory_0002"; +} + +/** + * @tc.name: RmDirectory_0003 + * @tc.desc: Test function of RmDirectory interface. + * @tc.size: MEDIUM + * @tc.type: FUNC + * @tc.level Level 1 + */ +HWTEST_F(MovedirMockTest, RmDirectory_0003, testing::ext::TestSize.Level1) +{ + GTEST_LOG_(INFO) << "MovedirMockTest-begin RmDirectory_0003"; + napi_env env = reinterpret_cast(0x1000); + napi_callback_info info = reinterpret_cast(0x1000); + error_code myEcRemove(MY_ERROR_VALUE, generic_category()); + filesystem::path myPath(g_movedirSrc); + tuple myMode(true, DIRMODE_DIRECTORY_REPLACE); + auto mock_ = MovedirMock::GetMock(); + + auto statMock = [](const char *path, struct stat *buf) { + buf->st_mode = S_IFDIR | 0755; + return 0; + }; + + testing::Sequence s; + EXPECT_CALL(*mock_, InitArgs(testing::_)).WillOnce(testing::Return(true)); + EXPECT_CALL(*mock_, ToUTF8StringPath()) + .WillOnce(testing::Return(JudgePath(g_movedirSrc))) + .WillOnce(testing::Return(JudgePath(g_movedirDest))); + EXPECT_CALL(*mock_, GetArgc()).WillOnce(testing::Return(NARG_CNT::THREE)); + EXPECT_CALL(*mock_, ToInt32(testing::_)).WillOnce(testing::Return(myMode)); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)).Times(4).InSequence(s).WillRepeatedly(testing::Invoke(statMock)); + EXPECT_CALL(*mock_, stat(testing::_, testing::_)) + .InSequence(s) + .WillOnce(testing::Return(-1)) + .WillOnce(testing::Invoke(statMock)) + .WillOnce(testing::Return(-1)); + EXPECT_CALL(*mock_, napi_create_array(testing::_, testing::_)).WillOnce(testing::Return(napi_invalid_arg)); + EXPECT_CALL(*mock_, ThrowErrAddData(testing::_, testing::_, testing::_)); + + auto res = MoveDir::Sync(env, info); + EXPECT_EQ(res, nullptr); + + GTEST_LOG_(INFO) << "MovedirMockTest-end RmDirectory_0003"; +} + +} // namespace Test +} // namespace ModuleFileIO +} // namespace FileManagement +} // namespace OHOS \ No newline at end of file