1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <fcntl.h>
17#include <gmock/gmock.h>
18#include <gtest/gtest.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <vector>
22
23#include "plugin_manager.h"
24#include "plugin_watcher.h"
25
26#include "logging.h"
27
28using namespace testing::ext;
29
30namespace {
31constexpr int DEAFULT_FILE_MODE = 0777;
32
33#if defined(__i386__) || defined(__x86_64__)
34const std::string DEFAULT_TEST_PATH = "./";
35#else
36const std::string DEFAULT_TEST_PATH_1 = "/data/local/tmp/watchertest/1/";
37const std::string DEFAULT_TEST_PATH_2 = "/data/local/tmp/watchertest/2/";
38const std::string DEFAULT_TEST_PATH_3 = "/data/local/tmp/watchertest/3/";
39const std::string NO_EXIST_TEST_PATH = "/data/local/tmp/noexist/";
40#endif
41
42class PluginWatchTest : public ::testing::Test {
43protected:
44    static constexpr int TEMP_DELAY = 10 * 1000;
45    static void SetUpTestCase() {}
46    static void TearDownTestCase() {}
47
48    void SetUp() override
49    {
50        std::string cmd = "mkdir -p " + DEFAULT_TEST_PATH_1;
51        system(cmd.c_str());
52        cmd = "mkdir -p " + DEFAULT_TEST_PATH_2;
53        system(cmd.c_str());
54        cmd = "mkdir -p " + DEFAULT_TEST_PATH_3;
55        system(cmd.c_str());
56        sort(expectFileList.begin(), expectFileList.end());
57    }
58
59    void TearDown() override
60    {
61        cmpFileList.clear();
62    }
63
64    void OnPluginAddedStub(const std::string& path)
65    {
66        cmpFileList.push_back(path);
67        sort(cmpFileList.begin(), cmpFileList.end());
68    }
69
70    void OnPluginRemovedStub(const std::string& path)
71    {
72        for (auto iter = cmpFileList.cbegin(); iter != cmpFileList.cend(); iter++) {
73            if (*iter == path) {
74                cmpFileList.erase(iter);
75                break;
76            }
77        }
78    }
79
80    void CreateFile(std::string dirPath) const
81    {
82        for (auto it : createFileList) {
83            int fd = creat((dirPath + it).c_str(), DEAFULT_FILE_MODE);
84            if (fd > 0) {
85                close(fd);
86            }
87        }
88    }
89
90    void AddFile(std::string dirPath) const
91    {
92        for (auto it : addFileList) {
93            int fd = creat((dirPath + it).c_str(), DEAFULT_FILE_MODE);
94            if (fd < 0) {
95                return;
96            }
97            write(fd, "testcase", 1);
98            close(fd);
99        }
100    }
101
102    void DeleteFile(std::string dirPath) const
103    {
104        for (auto it : createFileList) {
105            if (remove((dirPath + it).c_str()) != 0) {
106                return;
107            }
108        }
109        for (auto it : addFileList) {
110            if (remove((dirPath + it).c_str()) != 0) {
111                return;
112            }
113        }
114    }
115
116    bool CheckFileList(std::string dirPath) const
117    {
118        if (cmpFileList.size() != expectFileList.size()) {
119            return false;
120        }
121
122        for (size_t i = 0; i < cmpFileList.size(); i++) {
123            if (cmpFileList.at(i) != (dirPath + expectFileList.at(i))) {
124                return false;
125            }
126        }
127
128        return true;
129    }
130
131private:
132    std::vector<std::string> cmpFileList;
133
134    const std::vector<std::string> createFileList = {
135        "lib_6.so", "lib_5.so", "lib_8.so", "lib_4.so", "test1.txt"
136    };
137
138    const std::vector<std::string> addFileList = {
139        "libadd_6.so", "libadd_5.so", "libadd_8.so", "libadd_4.so", "test2.txt"
140    };
141
142    std::vector<std::string> expectFileList = {
143        "libadd_6.so", "libadd_5.so", "libadd_8.so", "libadd_4.so",
144        "lib_6.so",    "lib_5.so",    "lib_8.so",    "lib_4.so"
145    };
146};
147
148class MockPluginWatcher : public PluginWatcher {
149public:
150    explicit MockPluginWatcher(const PluginManagerPtr& pluginManager) : PluginWatcher(pluginManager) {}
151    ~MockPluginWatcher() = default;
152    MOCK_METHOD1(OnPluginAdded, void(const std::string&));
153    MOCK_METHOD1(OnPluginRemoved, void(const std::string&));
154};
155
156/**
157 * @tc.name: plugin
158 * @tc.desc: Monitor single plugin dir.
159 * @tc.type: FUNC
160 */
161HWTEST_F(PluginWatchTest, SingleWatchDirTest, TestSize.Level1)
162{
163    auto pluginManage = std::make_shared<PluginManager>();
164    MockPluginWatcher watcher(pluginManage);
165
166    EXPECT_CALL(watcher, OnPluginAdded(testing::_)).WillRepeatedly(
167        testing::Invoke(this, &PluginWatchTest::OnPluginAddedStub));
168    EXPECT_CALL(watcher, OnPluginRemoved(testing::_)).WillRepeatedly(
169        testing::Invoke(this, &PluginWatchTest::OnPluginRemovedStub));
170    CreateFile(DEFAULT_TEST_PATH_1);
171
172    EXPECT_TRUE(watcher.ScanPlugins(DEFAULT_TEST_PATH_1));
173    EXPECT_TRUE(watcher.WatchPlugins(DEFAULT_TEST_PATH_1));
174    usleep(TEMP_DELAY);
175    AddFile(DEFAULT_TEST_PATH_1);
176    usleep(TEMP_DELAY);
177    EXPECT_EQ(CheckFileList(DEFAULT_TEST_PATH_1), true);
178    DeleteFile(DEFAULT_TEST_PATH_1);
179    usleep(TEMP_DELAY);
180    EXPECT_EQ(cmpFileList.size(), 0);
181}
182
183/**
184 * @tc.name: plugin
185 * @tc.desc: Monitor multi plugin dir.
186 * @tc.type: FUNC
187 */
188HWTEST_F(PluginWatchTest, MultiWatchDirTest, TestSize.Level1)
189{
190    auto pluginManage = std::make_shared<PluginManager>();
191    MockPluginWatcher watcher(pluginManage);
192
193    EXPECT_CALL(watcher, OnPluginAdded(testing::_)).WillRepeatedly(
194        testing::Invoke(this, &PluginWatchTest::OnPluginAddedStub));
195    EXPECT_CALL(watcher, OnPluginRemoved(testing::_)).WillRepeatedly(
196        testing::Invoke(this, &PluginWatchTest::OnPluginRemovedStub));
197    CreateFile(DEFAULT_TEST_PATH_1);
198    EXPECT_TRUE(watcher.ScanPlugins(DEFAULT_TEST_PATH_1));
199    EXPECT_TRUE(watcher.WatchPlugins(DEFAULT_TEST_PATH_1));
200    usleep(TEMP_DELAY);
201    AddFile(DEFAULT_TEST_PATH_1);
202    usleep(TEMP_DELAY);
203    EXPECT_EQ(CheckFileList(DEFAULT_TEST_PATH_1), true);
204    DeleteFile(DEFAULT_TEST_PATH_1);
205    usleep(TEMP_DELAY);
206    EXPECT_EQ(cmpFileList.size(), 0);
207
208    CreateFile(DEFAULT_TEST_PATH_2);
209    EXPECT_TRUE(watcher.ScanPlugins(DEFAULT_TEST_PATH_2));
210    EXPECT_TRUE(watcher.WatchPlugins(DEFAULT_TEST_PATH_2));
211    usleep(TEMP_DELAY);
212    AddFile(DEFAULT_TEST_PATH_2);
213    usleep(TEMP_DELAY);
214    EXPECT_EQ(CheckFileList(DEFAULT_TEST_PATH_2), true);
215    DeleteFile(DEFAULT_TEST_PATH_2);
216    usleep(TEMP_DELAY);
217    EXPECT_EQ(cmpFileList.size(), 0);
218
219    CreateFile(DEFAULT_TEST_PATH_3);
220    EXPECT_TRUE(watcher.ScanPlugins(DEFAULT_TEST_PATH_3));
221    EXPECT_TRUE(watcher.WatchPlugins(DEFAULT_TEST_PATH_3));
222    usleep(TEMP_DELAY);
223    AddFile(DEFAULT_TEST_PATH_3);
224    usleep(TEMP_DELAY);
225    EXPECT_EQ(CheckFileList(DEFAULT_TEST_PATH_3), true);
226    DeleteFile(DEFAULT_TEST_PATH_3);
227    usleep(TEMP_DELAY);
228    EXPECT_EQ(cmpFileList.size(), 0);
229}
230
231/**
232 * @tc.name: plugin
233 * @tc.desc: Exception test.
234 * @tc.type: FUNC
235 */
236HWTEST_F(PluginWatchTest, ExceptionTest, TestSize.Level1)
237{
238    auto pluginManage = std::make_shared<PluginManager>();
239    MockPluginWatcher watcher(pluginManage);
240    EXPECT_FALSE(watcher.ScanPlugins(NO_EXIST_TEST_PATH));
241    EXPECT_FALSE(watcher.WatchPlugins(NO_EXIST_TEST_PATH));
242}
243} // namespace
244